Skip to main content

Database Locked: The Multiple Spring Boot Processes Problem

Β· 8 min read
Mahmut Salman
Software Developer

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​

StatusMeaningWhat It Means
RRunningCurrently executing on CPU
SSleepingWaiting for event (normal for web servers)
TStopped/SuspendedPaused (Ctrl+Z) but not terminated
ZZombieFinished but not cleaned up
DUninterruptible sleepWaiting 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:

  1. Accidentally pressed Ctrl+Z instead of Ctrl+C
  2. Closed terminal window without stopping process
  3. Started app multiple times without stopping previous instances
  4. 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:

  1. Press F4 (filter)
  2. Type java
  3. Navigate to your Spring Boot processes
  4. Press F9 (kill)
  5. Select signal 9 (SIGKILL)
  6. 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:

  1. H2 checks for .lock file
  2. If exists β†’ Another process is using it β†’ Error
  3. If not exists β†’ Creates .lock file β†’ 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​

  1. βœ… Use Ctrl+C to stop (not Ctrl+Z)
  2. βœ… Always verify process stopped before restarting
  3. βœ… Use process management scripts
  4. βœ… Use DevTools for development
  5. βœ… Use proper database in production (not H2)

Quick Reference​

TaskCommand
Find Java processesps aux | grep java
Kill specific processkill -9 <PID>
Kill all Java processespkill -9 java
Kill by patternpkill -9 -f "myapp.jar"
Check lock filels data/*.lock
Verify stoppedps 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