|
| 1 | +--- |
| 2 | +title: Caching |
| 3 | +description: Docs for the chunk caching system in FerrumC. |
| 4 | +--- |
| 5 | + |
| 6 | +FerrumC uses a caching system to ensure that chunk loading is as fast as possible, since that is one of the noticeable |
| 7 | +performance bottlenecks in Minecraft servers. However, the major bottleneck in sending chunks to client is the network |
| 8 | +delay which can't really be fixed, so we try to focus on reducing the load on the rest of the server and maintaining low |
| 9 | +memory usage. |
| 10 | + |
| 11 | +FerrumC uses the [moka](https://crates.io/crates/moka) crate to provide a tried and tested caching system. This crate |
| 12 | +provides both a sync and async cache, but FerrumC uses the async cache to prevent blocking the main thread. The general |
| 13 | +premise of caching is that it can store chunks in memory, reducing the amount of time spent reading chunks from the much |
| 14 | +slower disk storage. |
| 15 | + |
| 16 | +The cache is a simple key-value store, where the key is a 128-bit integer and the value is a `Chunk` struct. The key is |
| 17 | +generated from the dimension name, x coordinate and z coordinate of the chunk, the specifics of which are covered in the |
| 18 | +[database section](./database). This key is then used to interact with the cache, where the cache will return an |
| 19 | +`Option<Chunk>` when a chunk is requested. If the chunk is not in the cache, the cache will return `None`, and the chunk |
| 20 | +will need to be read from the database. |
| 21 | + |
| 22 | +All of this is handled automatically by the abstractions in `/src/lib/world/src/db_functions.rs`. These functions generally |
| 23 | +contain 2 parts: a crate-level internal function that fetches directly from the database, and a public function that fetches |
| 24 | +from the cache, falling back to the internal functions if the chunk can't be found. The public function will first check |
| 25 | +the cache for the chunk, and if it is not found, it will call the internal function to fetch the chunk from the database. |
| 26 | + |
| 27 | +FerrumC uses a very aggressive, simple caching system; any time a chunk is read or written, it is stored in the cache. |
| 28 | +This includes when a chunk is generated, loaded from disk, or modified. This means that the cache will often have a lot |
| 29 | +of unneeded chunks in it, but this results in very few cache misses for chunks that are frequently accessed. |
| 30 | + |
| 31 | +The cache is also limited in size, both by time and by the number of chunks. The cache will remove the oldest chunks |
| 32 | +when the cache reaches a certain size, and will also remove chunks when they have been in the cache for a certain amount |
| 33 | +without being accessed. For example if we set the cache capacity to 20mb and the TTL to 60 seconds (these are the defaults), |
| 34 | +the cache will remove the oldest chunks when the cache reaches 20mb, and will also remove chunks that have been in the cache |
| 35 | +for 60 seconds without being accessed. This means there is a hard cap of 20mb on the cache, and that chunks that are not |
| 36 | +frequently accessed will be removed from the cache to prevent unnecessary memory usage. |
| 37 | + |
| 38 | +Setting the TTL higher will allow the cache to store chunks for longer, but will also mean the chunk's idle memory usage |
| 39 | +will go down slower. This results in chunks being able to not be accessed for a longer time before being removed from the |
| 40 | +cache, but also means that the cache will use more memory. <br/> |
| 41 | +Setting the cache capacity higher will allow the cache to store more chunks, but will also mean that the cache will use more |
| 42 | +memory. Setting this higher is useful if you have many players online at once, as it will allow the cache to store more chunks |
| 43 | +and reduce the amount of time spent reading chunks from disk. |
0 commit comments