-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
227 additions
and
210 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,180 +1,134 @@ | ||
# 🎮 Player Module ![Version](https://img.shields.io/badge/version-1.0-blue) ![License](https://img.shields.io/badge/license-MIT-green) | ||
# 🚀 Cache Module | ||
|
||
> A **high-performance** and **scalable** player data management system. It leverages **L1 (Caffeine)** and **L2 (Redis)** caching, **MongoDB** persistence, and **Redis Streams** for near “enterprise-level” performance in distributed Minecraft or similar server environments. | ||
> A powerful multi-level caching solution supporting Caffeine and Redis, with functional programming support and automatic lock handling. | ||
--- | ||
## ✨ Features | ||
|
||
## 📚 Table of Contents | ||
- 🔄 Support for Caffeine's `Cache` and `AsyncCache` | ||
- 📦 In-memory Redis database implementation | ||
- 🔒 Automatic lock handling mechanism | ||
- 🎯 Functional programming support | ||
- 📚 Multi-level cache architecture | ||
- ⚡ High-performance operations | ||
- 🛡️ Thread-safe implementation | ||
|
||
## 📋 Table of Contents | ||
|
||
- [Installation](#-installation) | ||
- [Usage](#-usage) | ||
- [Caffeine Cache](#-caffeine-cache) | ||
- [Redis Cache](#-redis-cache) | ||
- [Custom Cache](#-custom-cache) | ||
- [Multi-Level Cache](#-multi-level-cache) | ||
- [Contributing](#-contributing) | ||
- [License](#-license) | ||
|
||
- [Introduction](#introduction) | ||
- [Automation](#automation) | ||
- [Key Components](#key-components) | ||
- [Data Flow](#data-flow) | ||
- [Performance Highlights](#performance-highlights) | ||
- [Why Near Enterprise-Level?](#why-near-enterprise-level) | ||
- [Summary](#summary) | ||
- [License](#license) | ||
## 📥 Installation | ||
|
||
--- | ||
Add the following dependency to your project: | ||
|
||
## Introduction | ||
```kotlin | ||
dependencies { | ||
compileOnly(files("libs/cache-1.0-SNAPSHOT-sources.jar")) | ||
} | ||
``` | ||
|
||
The **Player Module** is a core part of a distributed system aimed at managing player data with **high throughput** and **low latency**. By integrating: | ||
- **L1 Cache (Caffeine):** In-memory, ultra-fast access for online players. | ||
- **L2 Cache (Redis):** Central cache for multi-server environments, synchronized via Redis Streams. | ||
- **MongoDB:** Document-based persistence for flexible schema and long-term data storage. | ||
## 💻 Usage | ||
|
||
This architecture ensures that both frequent reads/writes (online players) and less common operations (offline queries, global sync) are handled efficiently. | ||
### 🔵 Caffeine Cache | ||
|
||
--- | ||
Caffeine provides high-performance caching capabilities with excellent hit rates. | ||
|
||
## Automation | ||
```java | ||
// Create Caffeine cache service | ||
CacheServiceInterface<Cache<Integer, String>, String> caffeineCache = | ||
CacheServiceFactory.createCaffeineCache(); | ||
|
||
- **L1 ↔ L2 Automatic Sync** | ||
- Whenever a player’s data is updated in L1, tasks like `L1ToL2PlayerDataSyncTask` push changes to L2. | ||
- Periodically or on-demand, Redis Streams also trigger updates across different servers. | ||
// Basic get operation | ||
String value = caffeineCache.get( | ||
cache -> cache.getIfPresent(1), // Get cache value | ||
() -> "defaultValue", // Cache miss handler | ||
(cache, queryValue) -> cache.put(1, queryValue), // Cache storage | ||
true // Enable caching | ||
); | ||
|
||
- **Automated Persistence** | ||
- The module uses timed tasks (e.g., `PlayerDataPersistenceTask`) to write L2 (Redis) data into MongoDB, ensuring eventual consistency without manual intervention. | ||
// Thread-safe operation with lock | ||
String valueWithLock = caffeineCache.get( | ||
cache -> new ReentrantLock(), // Lock acquisition | ||
cache -> cache.getIfPresent(1), // Cache retrieval | ||
() -> "defaultValue", // Cache miss handler | ||
(cache, queryValue) -> cache.put(1, queryValue), // Cache storage | ||
true, // Enable caching | ||
LockSettings.of(1, 1, TimeUnit.MINUTES) // Lock configuration | ||
); | ||
``` | ||
|
||
- **Annotation-Driven Registration** | ||
- Classes annotated with `@RStreamAccepterRegister` (like `L1ToL2PlayerDataSyncByNameRStreamAccepter`) automatically handle Redis Streams. | ||
- Type adapters annotated with `@TypeAdapterRegister` are auto-registered for custom serialization/deserialization (see `PairTypeAdapter`). | ||
|
||
--- | ||
### 🔴 Redis Cache | ||
|
||
## Key Components | ||
Redis cache service typically serves as a second-level cache for distributed systems. | ||
|
||
### LegacyPlayerDataService | ||
```java | ||
// Redis configuration | ||
Config config = new Config(); | ||
config.useSingleServer().setAddress("redis://127.0.0.1:6379"); | ||
|
||
A flexible “service object” encapsulating: | ||
- **Redis Connection** (L2 Cache + Streams) | ||
- **MongoDB Connection** (Persistence) | ||
- **Caffeine Cache** (L1 Cache) | ||
// Create Redis cache service | ||
RedisCacheServiceInterface redisCache = | ||
CacheServiceFactory.createRedisCache(config); | ||
|
||
By creating multiple `LegacyPlayerDataService` instances, you can manage different database or Redis clusters independently. | ||
> Internally, it uses multi-level caching: L1 for ultra-fast memory-based access, and L2 for cross-server data sharing. | ||
// Type-safe value retrieval | ||
int intValue = redisCache.getWithType( | ||
cache -> cache.getBucket("key").get(), | ||
() -> 1, | ||
(cache, queryValue) -> cache.getBucket("key").set(queryValue), | ||
true | ||
); | ||
``` | ||
|
||
### Caching Tiers | ||
### 🔧 Custom Cache | ||
|
||
1. **L1 (Caffeine)** | ||
- **Fast Memory Access** | ||
- Typically holds data for **online** or **recently active** players. | ||
- Minimizes network overhead and database queries. | ||
Implement your own cache using Java's `ConcurrentHashMap` or other solutions. | ||
|
||
2. **L2 (Redis)** | ||
- **Distributed, Centralized View** | ||
- Suited for multi-server setups. | ||
- Uses **Redis Streams** for broadcasting player data changes (e.g., join/quit, data update) across nodes. | ||
```java | ||
CacheServiceInterface<Map<Integer, String>, String> customCache = | ||
CacheServiceFactory.createCustomCache(new ConcurrentHashMap<>()); | ||
|
||
### MongoDB | ||
// Access the underlying cache implementation | ||
Map<Integer, String> cache = customCache.getCache(); | ||
``` | ||
|
||
- **Document-based Storage** | ||
- Efficient reads/writes for dynamic player data fields. | ||
- Durable final persistence layer if caches miss. | ||
### 📚 Multi-Level Cache | ||
|
||
--- | ||
The `FlexibleMultiLevelCacheService` provides sophisticated management of multiple cache levels. | ||
|
||
## Data Flow | ||
|
||
Below is a simplified, high-level view of how player data moves between caches and the database: | ||
|
||
1. **Player Data Retrieval** | ||
1. **L1 Cache Check** | ||
- If the player is **online** and data is in L1, return immediately (fastest path). | ||
- If **not found**, proceed to L2. | ||
2. **L2 Cache (Redis) Check** | ||
- If found, populate L1 for future quick lookups and return to caller. | ||
- If still **not found**, query MongoDB. | ||
3. **Database (MongoDB) Fetch** | ||
- On success, load into L1. | ||
- If the player has never been seen before, create a new record and store it in L1. | ||
|
||
2. **Player Logout & Data Sync** | ||
1. **Gather All `LegacyPlayerDataService` Instances** | ||
- Each has its own L1 + L2 caches. | ||
2. **Compare & Sync L1 → L2** | ||
- Any unsaved changes in L1 are written to L2. | ||
- Allows later scheduled tasks to persist them into MongoDB. | ||
3. **Remove Player Data from L1** | ||
- Frees up memory, avoids stale data if the player stays offline for a long time. | ||
|
||
3. **Data Synchronization & Persistence** | ||
1. **L1 → L2**: | ||
- `L1ToL2PlayerDataSyncTask` iterates all L1 entries, pushing to Redis if differences are found. | ||
- Runs periodically or on certain triggers (like logout). | ||
2. **L2 → MongoDB**: | ||
- `PlayerDataPersistenceTask` captures all data in Redis, writes it to MongoDB in a batched or scheduled manner. | ||
- Ensures final, durable storage of changes. | ||
|
||
4. **Player Data Update (Cross-Server)** | ||
1. **Publish to Redis Streams** | ||
- Using methods like `pubRStreamTask`, an update command is added to the stream (`RStreamTask`). | ||
- Example: `PlayerDataUpdateByNameRStreamAccepter` or `PlayerDataUpdateByUuidRStreamAccepter` consumes the stream and applies updates. | ||
2. **Other Servers Receive & Update** | ||
- If the player is online on another server, that server’s L1 cache is updated in real-time. | ||
- This ensures data consistency across the network. | ||
```java | ||
// Create primary cache (memory) | ||
CacheServiceInterface<Cache<String, String>, String> caffeineCache = | ||
CacheServiceFactory.createCaffeineCache(); | ||
|
||
--- | ||
// Initialize multi-level cache service | ||
FlexibleMultiLevelCacheService flexibleMultiLevelCacheService = | ||
CacheServiceFactory.createFlexibleMultiLevelCacheService(Set.of( | ||
TieredCacheLevel.of(1, caffeineCache.getCache()) | ||
)); | ||
``` | ||
|
||
## Performance Highlights | ||
## 🤝 Contributing | ||
|
||
- **High Throughput** | ||
- **Caffeine L1** eliminates network round-trips for frequent reads/writes. | ||
- **Redis L2** allows horizontal scaling with distributed caching. | ||
Contributions are welcome! Feel free to: | ||
|
||
- **Reduced DB Load** | ||
- Most lookups are fulfilled by L1/L2 caches, hitting MongoDB only on cache misses or scheduled persistence. | ||
- Frees the database from constant read pressure. | ||
- 🐛 Report bugs | ||
- 💡 Suggest new features | ||
- 📝 Improve documentation | ||
- 🔧 Submit pull requests | ||
|
||
- **Asynchronous Task Handling** | ||
- Redis Streams + `RStreamAccepterInterface` classes (e.g., `L1ToL2PlayerDataSyncByUuidRStreamAccepter`) process updates asynchronously, preventing blocking or contention. | ||
- Spreads out load across multiple servers. | ||
## 📄 License | ||
|
||
- **Document-Oriented Flexibility** | ||
- Using MongoDB for final storage avoids rigid schema definitions and supports easy extension of player attributes. | ||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. | ||
|
||
--- | ||
|
||
## Why Near Enterprise-Level? | ||
|
||
1. **Multilayer Caching**: | ||
- A pattern seen in large-scale enterprise systems: local (memory) + distributed caching for maximum throughput and minimal latency. | ||
|
||
2. **Distributed Messaging**: | ||
- **Redis Streams** act like a message queue/bus, commonly used in microservices or high-scale systems for cross-node sync and event broadcasting. | ||
|
||
3. **Concurrency Control**: | ||
- Uses distributed locks (Redisson) and concurrency settings (`LockSettings`) to ensure safe writes under high concurrency. | ||
|
||
4. **Annotation-Driven Architecture**: | ||
- Similar to Spring Boot or other enterprise frameworks, you declare components (`@RStreamAccepterRegister`, `@TypeAdapterRegister`) and let reflection + scanning wire them up. | ||
|
||
5. **Scalable & Modular**: | ||
- Multiple `LegacyPlayerDataService` can be spun up for different clusters or shards, each safely isolated. | ||
- Add or remove services as your player base grows. | ||
|
||
While labeled for “Minecraft servers,” the structure and design choices (multi-tier cache, streaming, concurrency, flexible data layer) align well with many enterprise-level backend systems. | ||
|
||
--- | ||
|
||
## Summary | ||
|
||
Bringing together **Caffeine** (L1 Cache), **Redis** (L2 Cache + Streams), and **MongoDB** (persistence), this module delivers: | ||
- **Ultrafast read/writes** for active players (Caffeine). | ||
- **Cross-instance data consistency** (Redis). | ||
- **Reliable final storage** (MongoDB). | ||
- **Seamless distribution** of updates (Redis Streams). | ||
- **Robust task scheduling** for synchronization and persistence. | ||
|
||
By instantiating a dedicated `LegacyPlayerDataService`, you can manage: | ||
- **Player session data** in memory, | ||
- **Multi-server sync** via Redis, | ||
- **Long-term data** in MongoDB, | ||
all while keeping overhead low and performance high. This design can scale from single-server scenarios to large distributed networks, ensuring minimal data conflicts and fast responses—essentially approaching an enterprise-grade architecture within the realm of Minecraft (or similar) server applications. | ||
|
||
--- | ||
Made with ❤️ by [LegacyLands Team](https://github.com/LegacyLands) | ||
|
||
## License | ||
|
||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. |
Oops, something went wrong.