Skip to main content

Multi-server

QuestsTracker supports multi-server synchronization via Redis. This page explains how to configure and maintain a multi-server deployment.

Single server?

If you only have one server, leave redis.enabled: false in your config.yml. The plugin works perfectly without Redis.

Architecture

Server 1 (Survival)     Server 2 (Adventure)     Server 3 (Event)
| | |
└───────────┬───────────┘───────────────────────┘
|
Redis Server
|
MySQL / MariaDB

All servers share:

  • The same MySQL/MariaDB database (via BetonQuest configuration)
  • The same Redis server for real-time synchronization

Requirements

ComponentVersionDescription
Redis6.0+Cache server and Pub/Sub messaging
MySQL/MariaDB8.0+ / 10.5+Shared database (configured in BetonQuest)
Network--Connectivity between all servers, Redis, and the database

Redis configuration

Installing Redis

# Ubuntu/Debian
sudo apt update && sudo apt install redis-server

# CentOS/RHEL
sudo yum install redis

# Docker
docker run -d --name redis -p 6379:6379 redis:latest

Securing Redis

For a production deployment:

# /etc/redis/redis.conf
requirepass your_redis_password
bind 0.0.0.0 # Or the specific server IP

Plugin configuration

On each server, configure config.yml:

redis:
enabled: true
host: "redis-address"
port: 6379
password: "your_redis_password"
Identical configuration

All servers must point to the same Redis server and the same BetonQuest database.

How synchronization works

Redis Pub/Sub channels

The plugin uses two communication channels:

ChannelDescription
quest-updatesStatus changes (activation, completion, progression)
quest-purgePlayer data purge for a package

Synchronization cycle

  1. A player completes a quest on Server 1
  2. Server 1 updates the database
  3. Server 1 publishes a message on the quest-updates channel
  4. Server 2 and Server 3 receive the message
  5. They invalidate their local cache for that player
  6. The next read uses up-to-date data from the database

Heartbeat system

When a player switches servers:

  1. The player disconnects from Server 1
  2. Redis data remains valid for 2 minutes (heartbeat)
  3. The player connects to Server 2
  4. Server 2 retrieves data from Redis (L2 cache -- fast)
  5. No need to reload from the database

If the player does not reconnect within 2 minutes, the Redis data expires and is reloaded from the database on the next connection.

Two-level cache

The plugin uses a two-level cache system for optimal performance:

LevelTypeLatencyTTLDescription
L1Caffeine (local)Sub-millisecond1-2 minIn-memory cache on each server
L2Redis (distributed)~1 ms2 minShared cache between servers

Read order

L1 (local) → L2 (Redis) → Database
  1. L1 -- Local in-memory cache (fastest)
  2. L2 -- Distributed Redis cache (on L1 miss)
  3. Database -- Source of truth (on L2 miss)

When data is loaded from the database, it is automatically cached in both L1 and L2.

Protection mechanisms

Circuit breaker

If Redis becomes unavailable:

  • After 3 consecutive failures, the circuit breaker activates
  • Redis operations fail silently (no error spam)
  • The plugin operates in database-only mode
  • 30-second cooldown before retrying
  • When Redis becomes available again, synchronization resumes automatically

Rate limiting

Redis publications are limited to 100ms debounce per player to prevent overloading the Redis server with rapid updates.

Connection pool

  • Maximum: 128 connections
  • Minimum idle: 16 connections
  • Test on borrow/return for reliability

Diagnostics

Checking the Redis connection

/kgquests redis

Displays:

  • Connection status (connected/disconnected)
  • Number of cached keys (progress, tracking, statuses)
  • Total keys

Checking a player's cache

/kgquests redis <player>

Displays:

  • Existence and TTL of progress keys
  • Existence and TTL of tracking keys
  • Number of status keys
  • Alert if keys are missing

Performance statistics

/kgquests stats

Displays hit rates per cache (L1 and L2).

Overall health

/kgquests health

Checks all components: database, Redis, cache, scoreboard.

Common issues

Redis unreachable

Symptom: /kgquests redis shows "Disconnected"

Solutions:

  1. Verify that Redis is running: redis-cli ping (should respond PONG)
  2. Check the firewall: port 6379 must be open between servers
  3. Verify the password in config.yml
  4. Check server logs for connection errors

Data desynchronized between servers

Symptom: Quests do not update when a player switches servers

Solutions:

  1. Verify that all servers point to the same Redis: /kgquests redis
  2. Verify that all servers use the same BetonQuest database
  3. Force a refresh: /kgquests refresh <player>
  4. Check the circuit breaker in the logs

High latency

Symptom: The menu or scoreboard is slow

Solutions:

  1. Check Redis latency: /kgquests benchmark
  2. Check database latency: /kgquests health
  3. Place Redis on the same network as your Minecraft servers (ideal latency < 1ms)
  4. Check cache hit rates: /kgquests stats

Production recommendations

Performance

  • Place Redis on the same network as your Minecraft servers (latency < 1ms)
  • Use MariaDB rather than MySQL for better performance
  • Monitor hit rates with /kgquests stats (target: >95%)

High availability

  • Configure Redis Sentinel or Redis Cluster for redundancy
  • Use a MySQL/MariaDB replica for backups
  • Monitor connections with /kgquests health

Backup

  • Regularly back up the MySQL/MariaDB database
  • Redis does not need to be backed up (data is in the database)
  • Export your config.yml and quests_config.yml

See also