00-Argonath-Wiki

Performance Optimization

Comprehensive guide to optimizing the Argonath Systems framework for production deployments.

Table of Contents

Overview

Performance optimization is critical for production quest systems that handle hundreds of concurrent players, thousands of quests, and millions of events. This guide covers proven optimization techniques for the Argonath framework.

Performance Goals

Profiling Quest Systems

JVM Profiling

package com.argonath.framework.performance;

import java.lang.management.*;

/**
 * JVM profiling utilities for quest systems.
 */
public class QuestProfiler {
    
    private static final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
    private static final MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
    
    /**
     * Profiles quest evaluation performance.
     */
    public static ProfileResult profileQuestEvaluation(Quest quest, UUID playerId) {
        // Enable CPU time tracking
        threadBean.setThreadCpuTimeEnabled(true);
        
        long startCpu = threadBean.getCurrentThreadCpuTime();
        long startMem = getCurrentMemoryUsage();
        long startTime = System.nanoTime();
        
        try {
            // Execute quest evaluation
            quest.evaluate(playerId);
            
        } finally {
            long endTime = System.nanoTime();
            long endCpu = threadBean.getCurrentThreadCpuTime();
            long endMem = getCurrentMemoryUsage();
            
            return new ProfileResult(
                endTime - startTime,           // Wall time
                endCpu - startCpu,             // CPU time
                endMem - startMem,             // Memory delta
                Thread.currentThread().getId()
            );
        }
    }
    
    private static long getCurrentMemoryUsage() {
        MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
        return heapUsage.getUsed();
    }
    
    /**
     * Profile result container.
     */
    public static class ProfileResult {
        public final long wallTimeNanos;
        public final long cpuTimeNanos;
        public final long memoryBytes;
        public final long threadId;
        
        ProfileResult(long wallTime, long cpuTime, long memory, long thread) {
            this.wallTimeNanos = wallTime;
            this.cpuTimeNanos = cpuTime;
            this.memoryBytes = memory;
            this.threadId = thread;
        }
        
        public double getWallTimeMs() {
            return wallTimeNanos / 1_000_000.0;
        }
        
        public double getCpuTimeMs() {
            return cpuTimeNanos / 1_000_000.0;
        }
        
        public double getMemoryKB() {
            return memoryBytes / 1024.0;
        }
        
        @Override
        public String toString() {
            return String.format(
                "Wall: %.2fms, CPU: %.2fms, Memory: %.2fKB, Thread: %d",
                getWallTimeMs(), getCpuTimeMs(), getMemoryKB(), threadId
            );
        }
    }
}

Condition Profiling

package com.argonath.framework.condition.profiling;

import com.argonath.framework.condition.api.*;

/**
 * Wrapper that profiles condition evaluation.
 */
public class ProfiledCondition implements Condition {
    
    private final Condition delegate;
    private final String conditionId;
    private final MetricsCollector metrics;
    
    // Statistics
    private final AtomicLong evaluationCount = new AtomicLong();
    private final AtomicLong totalTimeNanos = new AtomicLong();
    private final AtomicLong trueCount = new AtomicLong();
    
    public ProfiledCondition(Condition delegate, String id, MetricsCollector metrics) {
        this.delegate = delegate;
        this.conditionId = id;
        this.metrics = metrics;
    }
    
    @Override
    public boolean evaluate(ConditionContext context) {
        long startTime = System.nanoTime();
        
        try {
            boolean result = delegate.evaluate(context);
            
            if (result) {
                trueCount.incrementAndGet();
            }
            
            return result;
            
        } finally {
            long duration = System.nanoTime() - startTime;
            evaluationCount.incrementAndGet();
            totalTimeNanos.addAndGet(duration);
            
            // Record metrics
            metrics.recordConditionEvaluation(
                conditionId,
                delegate.getType(),
                duration
            );
        }
    }
    
    @Override
    public String getType() {
        return delegate.getType();
    }
    
    /**
     * Returns average evaluation time in nanoseconds.
     */
    public double getAverageTimeNanos() {
        long count = evaluationCount.get();
        return count > 0 ? (double) totalTimeNanos.get() / count : 0;
    }
    
    /**
     * Returns success rate (0.0 to 1.0).
     */
    public double getSuccessRate() {
        long count = evaluationCount.get();
        return count > 0 ? (double) trueCount.get() / count : 0;
    }
    
    /**
     * Returns profiling statistics.
     */
    public ConditionStats getStats() {
        return new ConditionStats(
            conditionId,
            delegate.getType(),
            evaluationCount.get(),
            totalTimeNanos.get(),
            trueCount.get()
        );
    }
}

Objective Profiling

package com.argonath.framework.objective.profiling;

/**
 * Profiled objective wrapper for performance analysis.
 */
public class ProfiledObjective implements Objective {
    
    private final Objective delegate;
    private final PerformanceMonitor monitor;
    
    private final AtomicLong updateCount = new AtomicLong();
    private final AtomicLong successfulUpdates = new AtomicLong();
    private final LongAdder totalUpdateTimeNanos = new LongAdder();
    
    @Override
    public boolean updateProgress(UUID playerId, ObjectiveEvent event) {
        long startTime = System.nanoTime();
        
        try {
            boolean updated = delegate.updateProgress(playerId, event);
            
            if (updated) {
                successfulUpdates.incrementAndGet();
            }
            
            return updated;
            
        } finally {
            long duration = System.nanoTime() - startTime;
            updateCount.incrementAndGet();
            totalUpdateTimeNanos.add(duration);
            
            monitor.recordObjectiveUpdate(
                delegate.getId(),
                delegate.getType(),
                duration
            );
        }
    }
    
    /**
     * Returns update throughput (updates per second).
     */
    public double getUpdateThroughput(long windowMs) {
        // Implementation would track updates over time window
        return updateCount.get() / (windowMs / 1000.0);
    }
}

Caching Strategies

Multi-Level Caching

package com.argonath.framework.cache;

import com.google.common.cache.*;
import java.util.concurrent.TimeUnit;

/**
 * Multi-level cache for quest data.
 * L1: In-memory hot cache (very fast, small)
 * L2: In-memory warm cache (fast, medium)
 * L3: Disk/Database (slower, large)
 */
public class QuestCache {
    
    // L1 Cache: Hot data, very short TTL
    private final Cache<String, Quest> hotCache;
    
    // L2 Cache: Warm data, longer TTL
    private final Cache<String, Quest> warmCache;
    
    // L3: Database/persistent storage
    private final StorageAdapter storage;
    
    public QuestCache(StorageAdapter storage) {
        this.storage = storage;
        
        // L1: 100 entries, 10 second TTL
        this.hotCache = CacheBuilder.newBuilder()
            .maximumSize(100)
            .expireAfterWrite(10, TimeUnit.SECONDS)
            .recordStats()
            .build();
            
        // L2: 1000 entries, 5 minute TTL
        this.warmCache = CacheBuilder.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(5, TimeUnit.MINUTES)
            .expireAfterAccess(2, TimeUnit.MINUTES)
            .recordStats()
            .build();
    }
    
    /**
     * Gets a quest, checking all cache levels.
     */
    public Optional<Quest> get(String questId) {
        // Check L1 (hot cache)
        Quest quest = hotCache.getIfPresent(questId);
        if (quest != null) {
            return Optional.of(quest);
        }
        
        // Check L2 (warm cache)
        quest = warmCache.getIfPresent(questId);
        if (quest != null) {
            // Promote to L1
            hotCache.put(questId, quest);
            return Optional.of(quest);
        }
        
        // Check L3 (storage)
        Optional<Quest> loaded = storage.loadQuest(questId);
        if (loaded.isPresent()) {
            quest = loaded.get();
            // Populate both caches
            warmCache.put(questId, quest);
            hotCache.put(questId, quest);
            return Optional.of(quest);
        }
        
        return Optional.empty();
    }
    
    /**
     * Puts a quest in all appropriate cache levels.
     */
    public void put(String questId, Quest quest) {
        hotCache.put(questId, quest);
        warmCache.put(questId, quest);
        storage.saveQuest(quest); // Async write-through
    }
    
    /**
     * Invalidates a quest across all cache levels.
     */
    public void invalidate(String questId) {
        hotCache.invalidate(questId);
        warmCache.invalidate(questId);
    }
    
    /**
     * Returns combined cache statistics.
     */
    public CacheStatistics getStats() {
        CacheStats l1Stats = hotCache.stats();
        CacheStats l2Stats = warmCache.stats();
        
        return new CacheStatistics(
            l1Stats.hitCount() + l2Stats.hitCount(),
            l1Stats.missCount() + l2Stats.missCount(),
            l1Stats.evictionCount() + l2Stats.evictionCount()
        );
    }
}

Query Result Caching

package com.argonath.framework.cache;

/**
 * Caches database query results with intelligent invalidation.
 */
public class QueryResultCache {
    
    private final LoadingCache<QueryKey, QueryResult> cache;
    private final Map<String, Set<QueryKey>> invalidationMap;
    
    public QueryResultCache(StorageAdapter storage) {
        this.invalidationMap = new ConcurrentHashMap<>();
        
        this.cache = CacheBuilder.newBuilder()
            .maximumSize(5000)
            .expireAfterWrite(30, TimeUnit.SECONDS)
            .recordStats()
            .build(new CacheLoader<QueryKey, QueryResult>() {
                @Override
                public QueryResult load(QueryKey key) {
                    return executeQuery(storage, key);
                }
            });
    }
    
    /**
     * Gets query results, using cache when possible.
     */
    public QueryResult get(String query, Object... params) {
        QueryKey key = new QueryKey(query, params);
        
        try {
            return cache.get(key);
        } catch (ExecutionException e) {
            throw new RuntimeException("Query execution failed", e);
        }
    }
    
    /**
     * Invalidates all queries related to a specific table.
     */
    public void invalidateTable(String tableName) {
        Set<QueryKey> keys = invalidationMap.get(tableName);
        if (keys != null) {
            cache.invalidateAll(keys);
            keys.clear();
        }
    }
    
    private QueryResult executeQuery(StorageAdapter storage, QueryKey key) {
        QueryResult result = storage.executeQuery(key.query, key.params);
        
        // Track which tables this query touches
        Set<String> tables = extractTableNames(key.query);
        for (String table : tables) {
            invalidationMap.computeIfAbsent(table, k -> ConcurrentHashMap.newKeySet())
                .add(key);
        }
        
        return result;
    }
    
    private static class QueryKey {
        final String query;
        final Object[] params;
        final int hashCode;
        
        QueryKey(String query, Object[] params) {
            this.query = query;
            this.params = params;
            this.hashCode = Objects.hash(query, Arrays.hashCode(params));
        }
        
        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof QueryKey)) return false;
            QueryKey other = (QueryKey) obj;
            return query.equals(other.query) && Arrays.equals(params, other.params);
        }
        
        @Override
        public int hashCode() {
            return hashCode;
        }
    }
}

Condition Result Caching

package com.argonath.framework.condition.cache;

/**
 * Caches condition evaluation results with smart invalidation.
 */
public class ConditionResultCache {
    
    private final Cache<ConditionCacheKey, Boolean> resultCache;
    private final Set<String> invalidationTriggers;
    
    public ConditionResultCache() {
        this.invalidationTriggers = ConcurrentHashMap.newKeySet();
        
        this.resultCache = CacheBuilder.newBuilder()
            .maximumSize(10000)
            .expireAfterWrite(60, TimeUnit.SECONDS)
            .recordStats()
            .build();
    }
    
    /**
     * Gets cached result or evaluates condition.
     */
    public boolean evaluate(Condition condition, ConditionContext context) {
        // Generate cache key
        ConditionCacheKey key = buildCacheKey(condition, context);
        
        Boolean cached = resultCache.getIfPresent(key);
        if (cached != null) {
            return cached;
        }
        
        // Cache miss - evaluate
        boolean result = condition.evaluate(context);
        
        // Only cache if condition is cacheable
        if (isCacheable(condition)) {
            resultCache.put(key, result);
        }
        
        return result;
    }
    
    /**
     * Invalidates cache based on event type.
     */
    public void invalidateOnEvent(String eventType) {
        if (invalidationTriggers.contains(eventType)) {
            resultCache.invalidateAll();
        }
    }
    
    private boolean isCacheable(Condition condition) {
        // Don't cache time-based or random conditions
        String type = condition.getType();
        return !type.contains("time") && !type.contains("random");
    }
    
    private ConditionCacheKey buildCacheKey(Condition condition, ConditionContext context) {
        return new ConditionCacheKey(
            condition.getType(),
            condition.serialize().toString(),
            context.getPlayer().map(p -> p.getUUID()).orElse(null)
        );
    }
}

Async Operations and Threading

Async Quest Evaluation

package com.argonath.framework.quest.async;

import java.util.concurrent.*;

/**
 * Asynchronous quest evaluation system.
 */
public class AsyncQuestEvaluator {
    
    private final ExecutorService evaluationPool;
    private final ScheduledExecutorService schedulerPool;
    
    public AsyncQuestEvaluator(int threadPoolSize) {
        // Main evaluation thread pool
        this.evaluationPool = new ThreadPoolExecutor(
            threadPoolSize,
            threadPoolSize * 2,
            60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(1000),
            new ThreadFactoryBuilder()
                .setNameFormat("quest-eval-%d")
                .setPriority(Thread.NORM_PRIORITY)
                .build(),
            new ThreadPoolExecutor.CallerRunsPolicy() // Backpressure
        );
        
        // Scheduler for periodic evaluations
        this.schedulerPool = Executors.newScheduledThreadPool(2,
            new ThreadFactoryBuilder()
                .setNameFormat("quest-scheduler-%d")
                .build()
        );
    }
    
    /**
     * Evaluates quest asynchronously.
     */
    public CompletableFuture<QuestResult> evaluateAsync(Quest quest, UUID playerId) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                return quest.evaluate(playerId);
            } catch (Exception e) {
                throw new CompletionException("Quest evaluation failed", e);
            }
        }, evaluationPool);
    }
    
    /**
     * Evaluates multiple quests in parallel.
     */
    public CompletableFuture<List<QuestResult>> evaluateBatch(
            List<Quest> quests, 
            UUID playerId) {
        
        List<CompletableFuture<QuestResult>> futures = quests.stream()
            .map(quest -> evaluateAsync(quest, playerId))
            .collect(Collectors.toList());
            
        return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
            .thenApply(v -> futures.stream()
                .map(CompletableFuture::join)
                .collect(Collectors.toList())
            );
    }
    
    /**
     * Schedules periodic quest evaluation.
     */
    public ScheduledFuture<?> schedulePeriodicEvaluation(
            Quest quest, 
            UUID playerId, 
            long period, 
            TimeUnit unit) {
        
        return schedulerPool.scheduleAtFixedRate(
            () -> {
                try {
                    quest.evaluate(playerId);
                } catch (Exception e) {
                    // Log error but don't stop scheduling
                    logger.error("Periodic evaluation failed", e);
                }
            },
            0,
            period,
            unit
        );
    }
    
    /**
     * Shuts down executor pools gracefully.
     */
    public void shutdown() {
        evaluationPool.shutdown();
        schedulerPool.shutdown();
        
        try {
            if (!evaluationPool.awaitTermination(30, TimeUnit.SECONDS)) {
                evaluationPool.shutdownNow();
            }
            if (!schedulerPool.awaitTermination(10, TimeUnit.SECONDS)) {
                schedulerPool.shutdownNow();
            }
        } catch (InterruptedException e) {
            evaluationPool.shutdownNow();
            schedulerPool.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}

Event Processing Pipeline

package com.argonath.framework.event.async;

import com.lmax.disruptor.*;
import com.lmax.disruptor.dsl.Disruptor;

/**
 * High-performance event processing using LMAX Disruptor.
 */
public class EventProcessingPipeline {
    
    private final Disruptor<ObjectiveEventWrapper> disruptor;
    private final RingBuffer<ObjectiveEventWrapper> ringBuffer;
    
    public EventProcessingPipeline(int bufferSize, int handlerThreads) {
        // Create disruptor with power-of-2 buffer size
        ThreadFactory threadFactory = new ThreadFactoryBuilder()
            .setNameFormat("event-handler-%d")
            .build();
            
        this.disruptor = new Disruptor<>(
            ObjectiveEventWrapper::new,
            bufferSize,
            threadFactory,
            ProducerType.MULTI,
            new BlockingWaitStrategy()
        );
        
        // Set up event handlers
        EventHandler<ObjectiveEventWrapper>[] handlers = 
            createHandlers(handlerThreads);
        disruptor.handleEventsWith(handlers);
        
        // Start the disruptor
        disruptor.start();
        this.ringBuffer = disruptor.getRingBuffer();
    }
    
    /**
     * Publishes an event to the pipeline.
     */
    public void publishEvent(ObjectiveEvent event) {
        ringBuffer.publishEvent((wrapper, sequence) -> {
            wrapper.event = event;
            wrapper.sequence = sequence;
        });
    }
    
    /**
     * Creates event handler array for parallel processing.
     */
    private EventHandler<ObjectiveEventWrapper>[] createHandlers(int count) {
        @SuppressWarnings("unchecked")
        EventHandler<ObjectiveEventWrapper>[] handlers = new EventHandler[count];
        
        for (int i = 0; i < count; i++) {
            handlers[i] = new ObjectiveEventHandler();
        }
        
        return handlers;
    }
    
    /**
     * Event handler that processes objective events.
     */
    private class ObjectiveEventHandler implements EventHandler<ObjectiveEventWrapper> {
        
        @Override
        public void onEvent(ObjectiveEventWrapper wrapper, long sequence, boolean endOfBatch) {
            try {
                processEvent(wrapper.event);
            } catch (Exception e) {
                logger.error("Failed to process event", e);
            }
        }
        
        private void processEvent(ObjectiveEvent event) {
            // Update all relevant objectives
            QuestManager.getInstance().notifyObjectiveEvent(
                event.getPlayerId(),
                event
            );
        }
    }
    
    /**
     * Event wrapper for disruptor ring buffer.
     */
    private static class ObjectiveEventWrapper {
        ObjectiveEvent event;
        long sequence;
    }
    
    public void shutdown() {
        disruptor.shutdown();
    }
}

Lock-Free Data Structures

package com.argonath.framework.concurrent;

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
 * Lock-free quest progress tracker.
 */
public class LockFreeProgressTracker {
    
    private volatile ProgressNode head;
    
    private static final AtomicReferenceFieldUpdater<LockFreeProgressTracker, ProgressNode> HEAD_UPDATER =
        AtomicReferenceFieldUpdater.newUpdater(
            LockFreeProgressTracker.class,
            ProgressNode.class,
            "head"
        );
    
    /**
     * Updates progress atomically without locks.
     */
    public void updateProgress(UUID playerId, String objectiveId, double progress) {
        ProgressNode newNode = new ProgressNode(playerId, objectiveId, progress);
        
        ProgressNode currentHead;
        do {
            currentHead = head;
            newNode.next = currentHead;
        } while (!HEAD_UPDATER.compareAndSet(this, currentHead, newNode));
    }
    
    /**
     * Gets current progress for player/objective.
     */
    public double getProgress(UUID playerId, String objectiveId) {
        ProgressNode current = head;
        
        while (current != null) {
            if (current.playerId.equals(playerId) && 
                current.objectiveId.equals(objectiveId)) {
                return current.progress;
            }
            current = current.next;
        }
        
        return 0.0;
    }
    
    private static class ProgressNode {
        final UUID playerId;
        final String objectiveId;
        final double progress;
        volatile ProgressNode next;
        
        ProgressNode(UUID playerId, String objectiveId, double progress) {
            this.playerId = playerId;
            this.objectiveId = objectiveId;
            this.progress = progress;
        }
    }
}

Database Query Optimization

Connection Pooling

package com.argonath.framework.storage;

import com.zaxxer.hikari.*;

/**
 * Optimized database connection pool configuration.
 */
public class DatabaseConnectionPool {
    
    private final HikariDataSource dataSource;
    
    public DatabaseConnectionPool(DatabaseConfig config) {
        HikariConfig hikariConfig = new HikariConfig();
        
        // Connection settings
        hikariConfig.setJdbcUrl(config.getJdbcUrl());
        hikariConfig.setUsername(config.getUsername());
        hikariConfig.setPassword(config.getPassword());
        
        // Pool sizing (rule of thumb: connections = cores * 2 + disk spindles)
        hikariConfig.setMaximumPoolSize(20);
        hikariConfig.setMinimumIdle(5);
        
        // Connection timeout
        hikariConfig.setConnectionTimeout(30000); // 30 seconds
        hikariConfig.setIdleTimeout(600000);      // 10 minutes
        hikariConfig.setMaxLifetime(1800000);     // 30 minutes
        
        // Performance settings
        hikariConfig.setCachePrepStmts(true);
        hikariConfig.setPrepStmtCacheSize(250);
        hikariConfig.setPrepStmtCacheSqlLimit(2048);
        hikariConfig.setUseServerPrepStmts(true);
        
        // Monitoring
        hikariConfig.setPoolName("QuestPool");
        hikariConfig.setRegisterMbeans(true);
        
        this.dataSource = new HikariDataSource(hikariConfig);
    }
    
    public Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
    
    public void shutdown() {
        if (dataSource != null && !dataSource.isClosed()) {
            dataSource.close();
        }
    }
    
    /**
     * Returns pool statistics.
     */
    public PoolStats getStats() {
        HikariPoolMXBean poolBean = dataSource.getHikariPoolMXBean();
        return new PoolStats(
            poolBean.getActiveConnections(),
            poolBean.getIdleConnections(),
            poolBean.getTotalConnections(),
            poolBean.getThreadsAwaitingConnection()
        );
    }
}

Batch Operations

package com.argonath.framework.storage.batch;

/**
 * Batch operations for efficient database updates.
 */
public class BatchQuestUpdater {
    
    private final Connection connection;
    private final int batchSize;
    
    public BatchQuestUpdater(Connection connection, int batchSize) {
        this.connection = connection;
        this.batchSize = batchSize;
    }
    
    /**
     * Updates multiple quest progress entries in batches.
     */
    public void updateProgressBatch(List<ProgressUpdate> updates) throws SQLException {
        String sql = "UPDATE quest_progress SET current = ?, updated_at = ? " +
                    "WHERE player_id = ? AND objective_id = ?";
        
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            int count = 0;
            
            for (ProgressUpdate update : updates) {
                stmt.setDouble(1, update.progress);
                stmt.setLong(2, System.currentTimeMillis());
                stmt.setString(3, update.playerId.toString());
                stmt.setString(4, update.objectiveId);
                stmt.addBatch();
                
                count++;
                
                // Execute batch when size threshold reached
                if (count % batchSize == 0) {
                    stmt.executeBatch();
                    stmt.clearBatch();
                }
            }
            
            // Execute remaining
            if (count % batchSize != 0) {
                stmt.executeBatch();
            }
        }
    }
    
    /**
     * Inserts multiple quest completions in a single transaction.
     */
    public void insertCompletionsBatch(List<QuestCompletion> completions) throws SQLException {
        connection.setAutoCommit(false);
        
        try {
            String sql = "INSERT INTO quest_completions " +
                        "(player_id, quest_id, completed_at, rewards) " +
                        "VALUES (?, ?, ?, ?)";
            
            try (PreparedStatement stmt = connection.prepareStatement(sql)) {
                for (QuestCompletion completion : completions) {
                    stmt.setString(1, completion.playerId.toString());
                    stmt.setString(2, completion.questId);
                    stmt.setLong(3, completion.timestamp);
                    stmt.setString(4, completion.rewardsJson);
                    stmt.addBatch();
                }
                
                stmt.executeBatch();
            }
            
            connection.commit();
            
        } catch (SQLException e) {
            connection.rollback();
            throw e;
        } finally {
            connection.setAutoCommit(true);
        }
    }
}

Query Optimization

package com.argonath.framework.storage.query;

/**
 * Optimized queries for common operations.
 */
public class OptimizedQuestQueries {
    
    /**
     * Gets active quests for a player (optimized with covering index).
     */
    public static final String GET_ACTIVE_QUESTS =
        "SELECT q.id, q.name, qp.progress, qp.updated_at " +
        "FROM quests q " +
        "INNER JOIN quest_progress qp ON q.id = qp.quest_id " +
        "WHERE qp.player_id = ? " +
        "  AND qp.completed = false " +
        "  AND qp.progress > 0 " +
        "ORDER BY qp.updated_at DESC " +
        "LIMIT ?";
    
    /**
     * Gets quest progress with objectives (single query instead of N+1).
     */
    public static final String GET_QUEST_WITH_OBJECTIVES =
        "SELECT " +
        "  q.id AS quest_id, " +
        "  q.name AS quest_name, " +
        "  q.description AS quest_desc, " +
        "  o.id AS obj_id, " +
        "  o.type AS obj_type, " +
        "  op.current AS obj_current, " +
        "  op.target AS obj_target " +
        "FROM quests q " +
        "LEFT JOIN objectives o ON q.id = o.quest_id " +
        "LEFT JOIN objective_progress op ON o.id = op.objective_id " +
        "  AND op.player_id = ? " +
        "WHERE q.id = ?";
    
    /**
     * Bulk query for multiple players' quest states.
     */
    public static final String GET_BULK_QUEST_STATES =
        "SELECT player_id, quest_id, state, progress " +
        "FROM quest_states " +
        "WHERE player_id IN (%s) " + // IN clause with placeholders
        "  AND quest_id = ? " +
        "  AND updated_at > ?";
    
    /**
     * Aggregated quest statistics (uses materialized view for performance).
     */
    public static final String GET_QUEST_STATS =
        "SELECT * FROM quest_statistics_mv WHERE quest_id = ?";
}

Database Indexing Strategy

-- Primary indexes for quest tables

-- Quest progress lookup by player
CREATE INDEX idx_quest_progress_player 
ON quest_progress(player_id, completed, updated_at);

-- Quest progress lookup by quest
CREATE INDEX idx_quest_progress_quest 
ON quest_progress(quest_id, player_id);

-- Objective progress covering index
CREATE INDEX idx_objective_progress_covering 
ON objective_progress(player_id, objective_id) 
INCLUDE (current, target, updated_at);

-- Quest completions for leaderboards
CREATE INDEX idx_completions_leaderboard 
ON quest_completions(quest_id, completed_at DESC);

-- Compound index for active quest queries
CREATE INDEX idx_active_quests 
ON quest_progress(player_id, completed, progress) 
WHERE completed = false AND progress > 0;

Memory Management

Object Pooling

package com.argonath.framework.memory;

import org.apache.commons.pool2.*;
import org.apache.commons.pool2.impl.*;

/**
 * Object pool for frequently created quest objects.
 */
public class QuestObjectPool {
    
    private final GenericObjectPool<ConditionContext> contextPool;
    private final GenericObjectPool<ObjectiveEvent> eventPool;
    
    public QuestObjectPool() {
        // Context pool configuration
        GenericObjectPoolConfig<ConditionContext> contextConfig = new GenericObjectPoolConfig<>();
        contextConfig.setMaxTotal(1000);
        contextConfig.setMaxIdle(100);
        contextConfig.setMinIdle(10);
        contextConfig.setBlockWhenExhausted(false);
        
        this.contextPool = new GenericObjectPool<>(
            new ConditionContextFactory(),
            contextConfig
        );
        
        // Event pool configuration
        GenericObjectPoolConfig<ObjectiveEvent> eventConfig = new GenericObjectPoolConfig<>();
        eventConfig.setMaxTotal(5000);
        eventConfig.setMaxIdle(500);
        eventConfig.setMinIdle(50);
        
        this.eventPool = new GenericObjectPool<>(
            new ObjectiveEventFactory(),
            eventConfig
        );
    }
    
    /**
     * Borrows a context from the pool.
     */
    public ConditionContext borrowContext() throws Exception {
        return contextPool.borrowObject();
    }
    
    /**
     * Returns a context to the pool.
     */
    public void returnContext(ConditionContext context) {
        contextPool.returnObject(context);
    }
    
    /**
     * Borrows an event from the pool.
     */
    public ObjectiveEvent borrowEvent() throws Exception {
        return eventPool.borrowObject();
    }
    
    /**
     * Returns an event to the pool.
     */
    public void returnEvent(ObjectiveEvent event) {
        eventPool.returnObject(event);
    }
    
    private static class ConditionContextFactory extends BasePooledObjectFactory<ConditionContext> {
        @Override
        public ConditionContext create() {
            return new PooledConditionContext();
        }
        
        @Override
        public PooledObject<ConditionContext> wrap(ConditionContext obj) {
            return new DefaultPooledObject<>(obj);
        }
        
        @Override
        public void passivateObject(PooledObject<ConditionContext> p) {
            // Reset object state before returning to pool
            ((PooledConditionContext) p.getObject()).reset();
        }
    }
}

Memory-Efficient Data Structures

package com.argonath.framework.memory;

import it.unimi.dsi.fastutil.objects.*;
import it.unimi.dsi.fastutil.longs.*;

/**
 * Memory-optimized quest progress storage using Fastutil.
 */
public class CompactProgressStorage {
    
    // Use primitive-based maps to reduce memory overhead
    private final Object2ObjectOpenHashMap<UUID, Long2DoubleOpenHashMap> playerProgress;
    
    public CompactProgressStorage() {
        this.playerProgress = new Object2ObjectOpenHashMap<>();
    }
    
    /**
     * Stores progress value (uses primitive double, not Double object).
     */
    public void setProgress(UUID playerId, long objectiveId, double progress) {
        playerProgress.computeIfAbsent(playerId, k -> new Long2DoubleOpenHashMap())
            .put(objectiveId, progress);
    }
    
    /**
     * Gets progress value.
     */
    public double getProgress(UUID playerId, long objectiveId) {
        Long2DoubleOpenHashMap objectives = playerProgress.get(playerId);
        return objectives != null ? objectives.get(objectiveId) : 0.0;
    }
    
    /**
     * Estimates memory usage in bytes.
     */
    public long estimateMemoryUsage() {
        long totalBytes = 0;
        
        // HashMap overhead: ~32 bytes per entry + key/value
        totalBytes += playerProgress.size() * (32 + 16); // UUID is 16 bytes
        
        // Inner maps
        for (Long2DoubleOpenHashMap inner : playerProgress.values()) {
            totalBytes += inner.size() * (8 + 8); // long key + double value
        }
        
        return totalBytes;
    }
}

Weak Reference Caching

package com.argonath.framework.memory;

import java.lang.ref.WeakReference;

/**
 * Cache that allows garbage collection when memory is low.
 */
public class WeakQuestCache {
    
    private final ConcurrentHashMap<String, WeakReference<Quest>> cache;
    private final ReferenceQueue<Quest> referenceQueue;
    
    public WeakQuestCache() {
        this.cache = new ConcurrentHashMap<>();
        this.referenceQueue = new ReferenceQueue<>();
        
        // Start cleanup thread
        startCleanupThread();
    }
    
    /**
     * Gets quest from cache, may return null if GC'd.
     */
    public Optional<Quest> get(String questId) {
        WeakReference<Quest> ref = cache.get(questId);
        if (ref != null) {
            Quest quest = ref.get();
            if (quest != null) {
                return Optional.of(quest);
            } else {
                // Reference was cleared, remove from map
                cache.remove(questId);
            }
        }
        return Optional.empty();
    }
    
    /**
     * Puts quest in cache with weak reference.
     */
    public void put(String questId, Quest quest) {
        cache.put(questId, new WeakReference<>(quest, referenceQueue));
    }
    
    /**
     * Cleanup thread that removes cleared references.
     */
    private void startCleanupThread() {
        Thread cleanup = new Thread(() -> {
            while (!Thread.interrupted()) {
                try {
                    // Wait for references to be cleared
                    referenceQueue.remove();
                    
                    // Remove all cleared references from map
                    cache.entrySet().removeIf(entry -> 
                        entry.getValue().get() == null
                    );
                    
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }, "quest-cache-cleanup");
        
        cleanup.setDaemon(true);
        cleanup.start();
    }
}

Network Optimization

Packet Batching

package com.argonath.framework.network;

/**
 * Batches multiple quest updates into single network packets.
 */
public class QuestUpdateBatcher {
    
    private final Map<UUID, List<QuestUpdate>> pendingUpdates;
    private final ScheduledExecutorService scheduler;
    private final int maxBatchSize;
    private final long flushIntervalMs;
    
    public QuestUpdateBatcher(int maxBatchSize, long flushIntervalMs) {
        this.pendingUpdates = new ConcurrentHashMap<>();
        this.maxBatchSize = maxBatchSize;
        this.flushIntervalMs = flushIntervalMs;
        
        this.scheduler = Executors.newScheduledThreadPool(1);
        scheduler.scheduleAtFixedRate(
            this::flushAll,
            flushIntervalMs,
            flushIntervalMs,
            TimeUnit.MILLISECONDS
        );
    }
    
    /**
     * Queues an update for batching.
     */
    public void queueUpdate(UUID playerId, QuestUpdate update) {
        List<QuestUpdate> updates = pendingUpdates.computeIfAbsent(
            playerId,
            k -> new CopyOnWriteArrayList<>()
        );
        
        updates.add(update);
        
        // Flush if batch size exceeded
        if (updates.size() >= maxBatchSize) {
            flush(playerId);
        }
    }
    
    /**
     * Flushes pending updates for a player.
     */
    private void flush(UUID playerId) {
        List<QuestUpdate> updates = pendingUpdates.remove(playerId);
        if (updates != null && !updates.isEmpty()) {
            sendBatchedUpdate(playerId, updates);
        }
    }
    
    /**
     * Flushes all pending updates.
     */
    private void flushAll() {
        Set<UUID> playerIds = new HashSet<>(pendingUpdates.keySet());
        for (UUID playerId : playerIds) {
            flush(playerId);
        }
    }
    
    private void sendBatchedUpdate(UUID playerId, List<QuestUpdate> updates) {
        // Create batched packet
        BatchedQuestUpdatePacket packet = new BatchedQuestUpdatePacket(updates);
        
        // Send to player
        NetworkManager.getInstance().sendPacket(playerId, packet);
    }
}

Best Practices for Large-Scale Deployments

1. Horizontal Scaling

/**
 * Distributed quest manager for multi-server deployments.
 */
public class DistributedQuestManager {
    
    private final RedisClient redis;
    private final String serverId;
    
    /**
     * Acquires distributed lock for quest modification.
     */
    public boolean acquireQuestLock(String questId, long timeoutMs) {
        String lockKey = "quest:lock:" + questId;
        return redis.setNX(lockKey, serverId, timeoutMs, TimeUnit.MILLISECONDS);
    }
    
    /**
     * Publishes quest event to all servers.
     */
    public void broadcastQuestEvent(QuestEvent event) {
        redis.publish("quest:events", serializeEvent(event));
    }
}

2. Data Partitioning

/**
 * Partitions quest data by player ID for scalability.
 */
public class PartitionedQuestStorage {
    
    private final int partitionCount;
    private final StorageAdapter[] partitions;
    
    /**
     * Gets partition for player.
     */
    private int getPartition(UUID playerId) {
        return Math.abs(playerId.hashCode() % partitionCount);
    }
    
    /**
     * Stores quest progress in appropriate partition.
     */
    public void storeProgress(UUID playerId, String questId, double progress) {
        int partition = getPartition(playerId);
        partitions[partition].storeProgress(playerId, questId, progress);
    }
}

3. Monitoring Integration

/**
 * Integration with monitoring systems.
 */
public class QuestMetricsReporter {
    
    private final MeterRegistry registry;
    
    public void recordQuestEvaluation(String questId, long durationNanos) {
        Timer.builder("quest.evaluation")
            .tag("quest_id", questId)
            .register(registry)
            .record(durationNanos, TimeUnit.NANOSECONDS);
    }
    
    public void recordCacheHit(String cacheType) {
        Counter.builder("quest.cache.hit")
            .tag("type", cacheType)
            .register(registry)
            .increment();
    }
}

Monitoring and Metrics

Performance Dashboards

/**
 * Quest system performance metrics.
 */
public class QuestPerformanceMetrics {
    
    // Evaluation metrics
    public Timer questEvaluationTimer;
    public Timer conditionEvaluationTimer;
    public Timer objectiveUpdateTimer;
    
    // Throughput metrics
    public Counter questStartCounter;
    public Counter questCompleteCounter;
    public Counter eventProcessedCounter;
    
    // Resource metrics
    public Gauge activeQuestsGauge;
    public Gauge cacheHitRateGauge;
    public Gauge databaseConnectionsGauge;
    
    // Error metrics
    public Counter evaluationErrorCounter;
    public Counter databaseErrorCounter;
    
    /**
     * Generates performance report.
     */
    public PerformanceReport generateReport() {
        return new PerformanceReport(
            questEvaluationTimer.mean(TimeUnit.MILLISECONDS),
            eventProcessedCounter.count(),
            cacheHitRateGauge.value(),
            evaluationErrorCounter.count()
        );
    }
}

Performance Benchmarks

Baseline Benchmarks

Configuration: 4-core CPU, 8GB RAM, PostgreSQL 14
Test Duration: 60 seconds
Concurrent Players: 100

Operation                    Throughput    Avg Latency    P95 Latency    P99 Latency
----------------------------------------------------------------------------------------
Quest Evaluation             15,000/sec    0.6ms          1.2ms          2.5ms
Condition Check              50,000/sec    0.2ms          0.4ms          0.8ms
Objective Update             20,000/sec    0.5ms          1.0ms          2.0ms
Quest Completion             5,000/sec     2.0ms          4.0ms          8.0ms
Database Query (cached)      100,000/sec   0.1ms          0.2ms          0.4ms
Database Query (uncached)    10,000/sec    1.0ms          2.5ms          5.0ms

Memory Usage:
- Active quests (1000): ~50MB
- Cache overhead: ~100MB
- Database connections: ~20MB
- Total: ~170MB

See Also