Skip to content

LEGACY: Caching & Persistence

Tyler edited this page Feb 6, 2021 · 1 revision

Overview

The NWN process is on a single thread. This means that any potentially long-running commands can create lag as the server is required to wait for that command to complete before it can do anything else - such as updating the game state, moving players, and so on. Numerous database calls create lag when bunched up in succession.

For this reason, we keep as much data in an in-memory cache as possible so that queries are far quicker than traditional database calls.

The Boot-Up Process

When the server launches, the first thing it does is read all data from relevant tables. This happens in the DataService.InitializeCache() method. Pretty much any data which needs to be read by the app should be included here. This excludes things like log entries and auditing records.

As data is read from the database, all of that information is placed into Cache objects which are located in the DataService.

The Cache Objects

Cache objects can be found in the Caching folder of SWLOR.Game.Server. Each of these cache objects organize the data in a way which is quickest to read. Generally they use C# Dictionary objects to accomplish this but if there's a more efficient way to structure the data, that can be used as well. It's up to the individual Cache object to determine the quickest method for retrieval.

All Cache objects must inherit from CacheBase, using the entity type as the generic parameter. For example,

This says "Define a cache for the Area entity type". Once the cache has been defined, it needs to be added as a property to the DataService for later retrieval. Here's an example:

Definition of the property in DataService:

Loading the Area cache in the InitializeCache method of DataService:

Inserting, Updating, and Deleting

The server is responsible for creating, updating, and removing entities as necessary. This is performed by making a call to DataService.SubmitDataChange. This method handles updating the cache and notifying the database thread that a change has been made to the data. We'll get to the database thread a little later but for now, rest assured that as long as you call this method the data will eventually be published to the database.

This method takes two arguments: the data object and the action type. The data object is pretty self-explanatory - it's the object you're modifying. For example, this could be an Area, a Perk, or any other entity you've set up a cache for.

The second argument is a DatabaseActionType. There are three options here: Insert, Update, or Delete. Be sure you use the right one because you might get corrupt data otherwise. Here are a few examples of each type:

Inserts a brand new PC apartment into the database and cache:

Updates an existing structure with new data:

Deletes an item from the database and cache:

Retrieving Data

So how do we get the data when we need it? As with most things, we need to access the DataService.

Recall that the DataService has properties for every cache available. Any time you need to retrieve data, you need to access it through these cache objects. Take the following snippet as an example.

This code is saying "Using the AreaCache, please get me the area with the specified ResRef. Then assign it to the variable dbArea".

It's very, very important to note that you are retrieving a COPY of that cached object, not the original. This means that any changes you make to the object WILL NOT PERSIST until you run DataService.SubmitDataChange. This is a security measure to ensure we don't inadvertently get the cache out of sync with the database.

Each cache has its own methods for retrieving data. You'll have to inspect each one of them to get an idea of what retrieval methods are available. You can also add your own methods as well.

The Database Thread

By this point you should know how to interface with the cache but what happens when changes are submitted? Behind the scenes there's a worker thread which processes all database changes sequentially, in the order they were received. You can find this worker thread in the Threading folder. The class name is DatabaseBackgroundThread.

As mentioned previously, when you submit database changes two things happen: the cache updates and the change is submitted to a queue which the DatabaseBackgroundThread processes in the order they were received.

Because of the asynchronous nature of the DatabaseBackgroundThread, you may not see changes reflected in the database for several seconds or even minutes after they're written. This is normal and nothing to be alarmed about. They will eventually get written.