This guide covers the database connection configuration for Security Shepherd, including the connection pooling implementation.
Security Shepherd uses two databases:
- MariaDB/MySQL - Main application database (user accounts, progress, modules)
- MongoDB - Challenge-specific data storage
Without connection pooling, every database operation requires:
- Opening a new TCP connection
- Performing authentication handshake
- Executing the query
- Closing the connection
This is expensive - each connection can take 20-50ms to establish, which adds up quickly under load.
| Benefit | Description |
|---|---|
| Performance | Connections are reused, eliminating connection overhead per request |
| Resource Efficiency | Limits total connections, preventing database overload |
| Connection Validation | Pool validates connections before use, catching stale connections |
| Configurable | Tune pool size, timeouts, and behavior for your workload |
| Monitoring | Pool provides statistics on usage, active connections, and wait times |
| Drawback | Description |
|---|---|
| Memory Overhead | Idle connections consume memory on both app and database servers |
| Configuration Complexity | Wrong pool size can cause issues (too small = contention, too large = resource waste) |
| Connection Leaks | Code that doesn't close connections properly can exhaust the pool |
| Stale Connections | Long-idle connections may be terminated by firewalls or the database |
| Debugging Complexity | Connection issues can be harder to diagnose with pooling |
Connection pooling is beneficial when:
- Your application handles many concurrent requests
- Database operations are frequent
- Connection establishment time is significant relative to query time
For Security Shepherd, pooling improves performance during:
- Multiple users accessing challenges simultaneously
- Scoreboard updates and leaderboard queries
- User authentication and session management
Security Shepherd uses HikariCP for MySQL/MariaDB connection pooling. HikariCP is a high-performance JDBC connection pool.
Benefits:
- Efficient connection reuse
- Reduced connection overhead
- Configurable pool size
- Connection validation and health checks
MongoDB connections use a singleton MongoClient instance. The MongoDB Java driver includes built-in connection pooling, so we maintain a single client instance that's reused across the application.
File: src/main/resources/database.properties
See example: src/main/resources/database.properties.example
# Database connection URL (without schema)
databaseConnectionURL=jdbc:mysql://localhost:3306/
# JDBC driver class
DriverType=org.gjt.mm.mysql.Driver
# Connection options
databaseOptions=useUnicode=true&character_set_server=utf8mb4
# Database schema name
databaseSchema=core
# Credentials
databaseUsername=root
databasePassword=your_password
# HikariCP Pool Settings (optional - defaults shown)
pool.maximumPoolSize=20
pool.minimumIdle=5
pool.connectionTimeout=5000
pool.idleTimeout=600000
pool.maxLifetime=1800000
pool.poolName=SecurityShepherdPool| Property | Default | Description |
|---|---|---|
pool.maximumPoolSize |
20 | Maximum number of connections in the pool |
pool.minimumIdle |
5 | Minimum number of idle connections to maintain |
pool.connectionTimeout |
5000 | Maximum time (ms) to wait for a connection |
pool.idleTimeout |
600000 | Maximum time (ms) a connection can be idle |
pool.maxLifetime |
1800000 | Maximum lifetime (ms) of a connection |
pool.leakDetectionThreshold |
60000 | Logs a warning if a connection is held longer than this (ms). Set to 0 to disable |
pool.poolName |
SecurityShepherdPool | Name for the pool (appears in logs) |
File: src/main/resources/mongo.properties
See example: src/main/resources/mongo.properties.example
# MongoDB connection settings
connectionHost=localhost
connectionPort=27017
databaseName=shepherdGames
connectTimeout=1000
socketTimeout=0
serverSelectionTimeout=30000
# Connection pool settings (optional)
connectionsPerHost=10
minConnectionsPerHost=2Connection pools are managed by DatabaseLifecycleListener, which:
- On application startup: Initializes the HikariCP connection pool
- On application shutdown: Closes all database connections gracefully
This is registered in web.xml:
<listener>
<listener-class>listeners.DatabaseLifecycleListener</listener-class>
</listener>The ConnectionPool class provides pool statistics:
String stats = ConnectionPool.getPoolStats();
// Returns: Pool: SecurityShepherdPool | Total: 10 | Active: 2 | Idle: 8 | Waiting: 0Connection pool events are logged at various levels:
- INFO: Pool initialization and shutdown
- DEBUG: Connection acquisition and pool statistics
- WARN: Connection validation failures
- ERROR: Pool initialization failures
When running with Docker, database hosts are configured via environment variables in .env:
# MariaDB
CONTAINER_MARIADB=secshep_mariadb
DB_PASS=your_password
DB_PORT=3306
# MongoDB
CONTAINER_MONGO=secshep_mongoThe Tomcat container connects to databases using container names (e.g., secshep_mariadb) as hostnames within the Docker network.
After starting the stack with docker compose up, the app will redirect to https://localhost/setup.jsp for initial database configuration.
Note: The TLS certificate is self-signed. Your browser will show a security warning — accept it to proceed.
The setup page requires a token from the server's filesystem to prevent unauthorized configuration:
docker exec secshep_tomcat cat /usr/local/tomcat/conf/SecurityShepherd.authNavigate to https://localhost/setup.jsp and fill in:
| Field | Value | Notes |
|---|---|---|
| Hostname | secshep_mariadb |
The Docker container name — not localhost |
| Port | 3306 |
|
| DB Username | root |
|
| DB Password | CowSaysMoo |
Must match DB_PASS in .env |
| Override Databases | checked | Initializes all challenge schemas on first setup |
| MongoDB Host | secshep_mongo |
The Docker container name — not localhost |
| MongoDB Port | 27017 |
|
| Authentication token | (paste from step 1) |
Click submit. On success you will see "Database Configuration Complete" and be redirected to the login page.
AUTH=$(docker exec secshep_tomcat cat /usr/local/tomcat/conf/SecurityShepherd.auth)
curl -k -s -X POST https://localhost/setup \
-d "dbhost=secshep_mariadb" \
-d "dbport=3306" \
-d "dbuser=root" \
-d "dbpass=CowSaysMoo" \
-d "dboverride=override" \
-d "dbauth=$AUTH" \
-d "mhost=secshep_mongo" \
-d "mport=27017"Important: The hostnames must be Docker container names (e.g.
secshep_mariadb,secshep_mongo), notlocalhost. The Tomcat container connects to the databases over the Docker network, where containers are addressed by name.
If you see "Connection is not available" errors:
- Check for connection leaks (connections not being closed)
- Increase
pool.maximumPoolSize - Review
pool.connectionTimeoutsetting
If connections are slow to acquire:
- Check database server health
- Review network latency between app and database
- Consider increasing
pool.minimumIdle
If connections are failing validation:
- Check database server is running
- Verify credentials in properties file
- Check network connectivity
If the MariaDB container exits immediately on first startup with an error like:
ERROR 1064 (42000) at line 181: You have an error in your SQL syntax;
check the manual that corresponds to your MariaDB server version for the right syntax to use near '' at line 3
This is a stale Docker image cache issue. The SQL source files in src/main/resources/database/ have DELIMITER statements commented out for compatibility with tools that don't support DELIMITER. During the Maven build (mvn -Pdocker), a script (docker/scripts/convert-sql-scripts.sh) uncomments them in the copies under docker/mariadb/target/. If Docker reuses a cached image from before this conversion ran, the stored procedures fail to parse.
Fix:
mvn -Pdocker validate # ensure SQL scripts are converted
docker compose build --no-cache db # rebuild without cache
docker compose down -v # remove old volumes with failed init
docker compose up -d db # start freshNote: docker compose down -v is needed because MariaDB only runs init scripts on first startup with an empty data volume. If the previous attempt partially initialized, the scripts won't re-run without removing the volume.