Database Locked: The Multiple Spring Boot Processes Problem
Your Spring Boot app won't start and you see a "database is locked" error. The culprit? Multiple instances of your application running simultaneously, all trying to access the same database file. Here's how to identify and fix this common problem.
The Problemβ
You try to start your Spring Boot application and see:
org.h2.jdbc.JdbcSQLNonTransientConnectionException:
Database may be already in use: "Locked by another process"
What's happening?
- Multiple Spring Boot processes are running
- Each is trying to lock the same H2 database file
- Only ONE process can hold the lock at a time
- Your new startup fails because the file is already locked
Finding the Culprit Processesβ
Check Running Java Processesβ
ps aux | grep java
Example output:
user 31586 0.0 1.2 /usr/bin/java -jar myapp.jar T
user 23013 2.5 3.4 /usr/bin/java -jar myapp.jar S
user 12777 0.0 1.1 /usr/bin/java -jar myapp.jar T
Three processes found! Let's decode what we're seeing:
PID %CPU %MEM COMMAND STATUS
31586 0.0 1.2 /usr/bin/java -jar myapp.jar T (Suspended)
23013 2.5 3.4 /usr/bin/java -jar myapp.jar S (Running)
12777 0.0 1.1 /usr/bin/java -jar myapp.jar T (Suspended)
Understanding Process Statusβ
| Status | Meaning | What It Means |
|---|---|---|
| R | Running | Currently executing on CPU |
| S | Sleeping | Waiting for event (normal for web servers) |
| T | Stopped/Suspended | Paused (Ctrl+Z) but not terminated |
| Z | Zombie | Finished but not cleaned up |
| D | Uninterruptible sleep | Waiting for I/O (disk, network) |
In this case:
- T status = Process was suspended (probably Ctrl+Z)
- S status = Process is running normally
- All three are holding onto the database file lock!
The Root Cause: Suspended Processesβ
How Processes Get Suspendedβ
# Terminal 1
$ mvn spring-boot:run
[INFO] Application started...
# You press Ctrl+Z (instead of Ctrl+C)
^Z
[1]+ Stopped mvn spring-boot:run
What happened:
- β Process is suspended (not terminated)
- β Still holds database file lock
- β Still consumes memory
- β Won't respond to new requests
- β Blocks new instances from starting
Common scenarios:
- Accidentally pressed Ctrl+Z instead of Ctrl+C
- Closed terminal window without stopping process
- Started app multiple times without stopping previous instances
- IDE restarted app without killing old process
The Solution: Kill All Processesβ
Method 1: Kill Individual Processesβ
# Kill each process by PID
kill -9 31586
kill -9 23013
kill -9 12777
Flags explained:
kill= Send termination signal-9= SIGKILL (force kill, cannot be ignored)PID= Process ID
Verify they're gone:
ps aux | grep java
Should show no results (or only your current session).
Method 2: Kill All Java Processes (Nuclear Option)β
# Kill ALL Java processes (use with caution!)
pkill -9 java
β οΈ Warning: This kills ALL Java processes, including:
- Your IDE (if it's Java-based like IntelliJ)
- Other Java applications
- Only use if you're sure!
Method 3: Kill by Pattern Matchingβ
# Kill only Spring Boot applications
pkill -9 -f "spring-boot"
# Or kill by jar name
pkill -9 -f "myapp.jar"
Safer than killing all Java processes!
Method 4: Interactive Process Manager (htop)β
# Install htop (if not already installed)
brew install htop # macOS
sudo apt install htop # Linux
# Run htop
htop
In htop:
- Press
F4(filter) - Type
java - Navigate to your Spring Boot processes
- Press
F9(kill) - Select signal
9 (SIGKILL) - Press Enter
Understanding Database File Lockingβ
Why H2 Locks the Fileβ
Spring Boot Process 1
β
Opens database file
β
Acquires EXCLUSIVE LOCK
β
data/myapp.mv.db [π LOCKED]
β
Spring Boot Process 2 tries to open
β
β Error: "Database may be already in use"
H2's file locking mechanism:
data/
βββ myapp.mv.db β Database file
βββ myapp.mv.db.lock β Lock file (created when in use)
When app starts:
- H2 checks for
.lockfile - If exists β Another process is using it β Error
- If not exists β Creates
.lockfile β Proceed
Lock file contents:
#FileLock
#Thu Oct 18 10:30:15 PDT 2024
server=192.168.1.100\:5000
id=1234567890abcdef
method=file
Prevention Strategiesβ
Strategy 1: Always Use Ctrl+C (Not Ctrl+Z)β
# β
CORRECT: Stop the process
mvn spring-boot:run
# Press Ctrl+C when done
^C
[INFO] BUILD SUCCESS
# β WRONG: Suspend the process
mvn spring-boot:run
# Press Ctrl+Z
^Z
[1]+ Stopped mvn spring-boot:run β Process still running!
Remember:
- Ctrl+C = Terminate (kill)
- Ctrl+Z = Suspend (pause, keeps running)
Strategy 2: Use Process Management Scriptsβ
startup.sh:
#!/bin/bash
# Check if already running
if pgrep -f "myapp.jar" > /dev/null; then
echo "β Application is already running!"
echo "Run 'pkill -f myapp.jar' to stop it first."
exit 1
fi
# Start application
echo "β
Starting application..."
java -jar target/myapp.jar
shutdown.sh:
#!/bin/bash
# Kill by jar name
pkill -f "myapp.jar"
# Verify stopped
if ! pgrep -f "myapp.jar" > /dev/null; then
echo "β
Application stopped successfully"
else
echo "β οΈ Application still running, force killing..."
pkill -9 -f "myapp.jar"
fi
Strategy 3: Use Spring Boot DevToolsβ
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
Benefits:
- Automatic restart on code changes
- Better process management
- Automatic cleanup on shutdown
Strategy 4: Use Unique Database Files per Sessionβ
# application.properties
# Use timestamp in database name
spring.datasource.url=jdbc:h2:file:./data/myapp-${random.uuid}
Or:
# Pass database name as argument
mvn spring-boot:run -Dspring.datasource.url=jdbc:h2:file:./data/myapp-dev
Debugging Commands Cheat Sheetβ
Find Running Processesβ
# All Java processes
ps aux | grep java
# Spring Boot processes only
ps aux | grep "spring-boot"
# By jar name
ps aux | grep "myapp.jar"
# Show only PID and command
pgrep -fl java
Check Process Detailsβ
# Detailed info about specific PID
ps -fp 31586
# Process tree (see parent-child relationships)
pstree -p 31586
# Full command line
ps -o cmd= -p 31586
Kill Processesβ
# Graceful termination (SIGTERM)
kill 31586
# Force kill (SIGKILL)
kill -9 31586
# Kill all matching pattern
pkill -f "myapp.jar"
# Kill all Java processes
pkill java # or pkill -9 java for force
Verify Database File Lockβ
# Check if lock file exists
ls -la data/*.lock
# See what's locking the file (macOS)
lsof data/myapp.mv.db
# See what's locking the file (Linux)
fuser data/myapp.mv.db
Real-World Scenarioβ
The Problemβ
$ mvn spring-boot:run
[ERROR] Database locked: ./data/ecommerce_db
[ERROR] Another process is using the database
Step 1: Find the Culpritsβ
$ ps aux | grep java
user 31586 T java -jar myapp.jar
user 23013 S java -jar myapp.jar
user 12777 T java -jar myapp.jar
Analysis:
- 3 processes found
- 2 suspended (T), 1 running (S)
- All holding database lock
Step 2: Kill All Processesβ
$ kill -9 31586 23013 12777
Or:
$ pkill -9 -f "myapp.jar"
Step 3: Verify Cleanupβ
$ ps aux | grep java
# Should show no results
$ ls data/*.lock
# Should show "No such file or directory"
Step 4: Restart Cleanβ
$ mvn spring-boot:run
[INFO] Starting application...
[INFO] Tomcat started on port 8080
[INFO] Application started successfully β
Common Mistakes and Fixesβ
Mistake 1: Using Ctrl+Z Instead of Ctrl+Cβ
Problem:
$ mvn spring-boot:run
^Z
[1]+ Stopped mvn spring-boot:run
Fix:
# Resume the suspended job
$ fg
# Then properly terminate with Ctrl+C
^C
Mistake 2: Closing Terminal Without Stopping Appβ
Problem:
- Close terminal window
- Process keeps running in background
- Still holds database lock
Fix:
# Before closing terminal, always stop the app
$ mvn spring-boot:run
# When done:
^C # Ctrl+C to stop
# Or if you already closed terminal:
$ ps aux | grep java
$ kill -9 <PID>
Mistake 3: Running from IDE + Command Lineβ
Problem:
- Start app from IntelliJ
- Also start with
mvn spring-boot:run - Both try to use same database
Fix:
# Check IDE status first
# Stop any running configurations in IDE
# Then start from command line
# Or use different database files:
# IDE: jdbc:h2:file:./data/myapp-ide
# CLI: jdbc:h2:file:./data/myapp-cli
Production Considerationsβ
Don't Use H2 in Productionβ
# β Development (H2 - file-based)
spring.datasource.url=jdbc:h2:file:./data/myapp
# β
Production (PostgreSQL - client-server)
spring.datasource.url=jdbc:postgresql://db-server:5432/myapp
Why?
- H2 file locking is for single-user scenarios
- Production needs multi-user concurrent access
- Use PostgreSQL, MySQL, or other client-server databases
Use Process Managers in Productionβ
# systemd service (Linux)
sudo systemctl start myapp
sudo systemctl stop myapp
sudo systemctl status myapp
# Docker (recommended)
docker run -d --name myapp myapp:latest
docker stop myapp
docker ps
Summaryβ
The Problemβ
Multiple Spring Boot processes β All try to lock database file β Error
The Solutionβ
# 1. Find processes
ps aux | grep java
# 2. Kill them
kill -9 <PID1> <PID2> <PID3>
# 3. Verify cleanup
ps aux | grep java
# 4. Restart clean
mvn spring-boot:run
Preventionβ
- β Use Ctrl+C to stop (not Ctrl+Z)
- β Always verify process stopped before restarting
- β Use process management scripts
- β Use DevTools for development
- β Use proper database in production (not H2)
Quick Referenceβ
| Task | Command |
|---|---|
| Find Java processes | ps aux | grep java |
| Kill specific process | kill -9 <PID> |
| Kill all Java processes | pkill -9 java |
| Kill by pattern | pkill -9 -f "myapp.jar" |
| Check lock file | ls data/*.lock |
| Verify stopped | ps aux | grep java |
Remember: One database file, one process at a time. Keep it simple, keep it clean! π§Ή
Tags: #spring-boot #troubleshooting #database #h2 #process-management #debugging
