Okay. I don’t think sharding is easy but I also know very little about it. Amethyst’s entity index and component storage tables are not exposed and it’s not implemented as a proper SQL-style database (the the tables are private Vec
s allocated on the heap), so you will have to write database management and sharding yourself.
Setting up Shards
I’m assuming you would have all the servers hooked up over LAN. The number of servers and their machine identifiers (MAC addresses, local IP addresses, what have you) can be read from config files.
The thing is, regions (and World
s in general) are going to be created on the fly. Somehow the software has to decide, on the fly, which server will create each new World
. I don’t know how to make that decision, maybe you can base it on some sort of runtime profiling or maybe there’s a way to let the server operator configure it. I don’t know.
I can think of a way to implement the sharding once it is decided where it will go. Wherever above I wrote Option<World>
, replace that with Option<Shard>
. Shard
would be a struct that contains an identifier for, and owned TCP Stream/Listener to communicate with, the server that will contain the new World
.
Each method for spec’s World
will be wrapped by Shard
to send an appropriate EXEC
message down the channel (see the next section). I think Rust allows you to go from strings to function calls but if not an enum or static array of closures can match procedure strings from EXEC
messages to functions.
For their part, all secondary servers will run the server executable with either a command-line argument or a config file option that puts them in shard mode. In this mode they do not run a full server, but instead listen for messages on a network channel.
Server-Server Interface (within a cluster or between threads on the same server)
AFAIK you have two options,
- write your own shared memory model
- write your own messaging model
Even the Rust book says shared memory sucks, so that leaves messaging. The good news is that the Rust standard library has TCP tools, and Specs has built-in serialization (for the purpose of saving/loading games).
There’s only a few messages you really have to implement. Those are, in SQL terms,
-
DELETE
: When a system wants to remove data from another World
-
EXEC
: When a system wants to call a procedure on another World
-
INSERT
: When a system wants to add data to another World
-
SELECT
: When a system requires data from another World
-
UPDATE
: When a system wants to update data on another World
Client-Server Interface
As far as the client->server interface goes, you have to decide on frontend or backend access.
- frontend access: client->frontend->shard
- backend access: client->shard
I think for simplicity’s sake you should use a single point of access (frontend) so the client only uses one interface regardless of server clustering. You could implement both and let the server operator decide, but that seems like a waste of dev time.