Multithreading

Overview

BGDatabase itself is not thread-safe, it's meant to be called from main thread only (from MonoBehaviour scripts)

However there are 2 scenarios, you could use BGDatabase in multi-threaded environment (for example, with Unity ECS).

  1. Use data from BGDatabase and create Unity ECS entities. Merge changes back to database on save if needed.
  2. Use multi-threaded add-on

Scenario # 1

  1. Make sure you call database only from main thread (e.g. from MonoBehaviour classes)
  2. Create Unity ECS entities out of database information from MonoBehaviour class
  3. If you need some of these Unity ECS entities to be persistent, (e.g. they needs to be saved/loaded), store them back to database the moment they needs to be persisted, e.g, when the game needs to be saved. Run saving/loading on main thread.
  4. If you need to call database from multithreaded environment, use BGMainThreadRunner.RunOnMainThread(Action action). This will add action to the jobs list to be run on main thread. Make sure you call at sparingly, at some relatively rare condition, otherwise it can become a bottleneck.

Scenario # 2 (MT addon overview)

Use multi-threading addon (MT addon). Multi-threading addon creates environment, which can be accessed from multiple threads safely, by coping chosen tables data into special data containers.

Scenario # 2 (How MT addon works?)

Scenario # 2 (What can I do with MT addon?)

  1. You can chose which tables you want to be accessible for multi-threading operations.
  2. You can merge changes back to main database either manually or auto (on saving). Otherwise changes are not synchronized. Merging changes back is pretty expensive operation.
  3. MT addon has it's own code generator and API very similar to single-threaded API.
  4. There are 2 types of transactions: lightweight, lock-free readonly transactions and very heavy write transactions. If you need to update data very often, please, consider scenario # 1 instead.

Scenario # 2 (What I need to know before using MT Addon?)

  1. Lists fields, Unity assets fields and fields, which reference Unity objects are not supported.
  2. All entities are structs, not classes. If result can be null, BGMTEntity? is used
  3. Entities are only marked as deleted inside transaction, the actual removal takes place at the end of transaction
  4. Write transactions can be executed asynchronously, so do not expect changes take effect right after the method call. If you need some code to be run after transaction finish, use (Action callback) as second parameter to BGRepo.M.Write method

Scenario # 2 (How to use MT addon?)

We tried our best to make multithreaded API very similar to single-threaded one. Also, we provide code generation for MT addon. Start working with multithreading environment by creating read-only or write transactions.

        //start read-only transaction (option 1)
        BGMTRepo mtRepo = BGRepo.M.RepoReadOnly;

	    //start read-only transaction (option 2)
	    BGRepo.M.Read(mtRepo =>
        {
            //access data from mtRepo here
        });

	    //start write transaction (the only option)
	    BGRepo.M.Write(mtRepo =>
        {
            // you can read and change data here.
			// This transaction can be run asynchronously (on separate thread),
			// so do not expect changes take effect right after the method call
        });

BGMTRepo has very similar methods to ones single-threaded API has. For example, to iterate through "Items" table rows, which have "damage" column value more than 10:

        BGRepo.M.Read(mtRepo =>
        {
            mtRepo["Items"].ForEachEntity(entity =>
            {
                print("Weapon: " + entity.Name);
            }, entity => entity.Get<int>("damage") > 10);
        });

And just like with single-threaded API, Code generation greatly simplify access to database by removing all boilerplate code

		BGRepo.M.Read(mtRepo =>
		{
			Items.ForEachEntity(mtRepo, item =>
			{
				print("Weapon: " + item.name);
			}, item => item.damage > 10);
		});