[erlang-questions] Desing of MVC models
Ludovic Demblans
ludovic@REDACTED
Thu Sep 12 23:59:22 CEST 2013
Hi,
I would like to ask a few questions about models (in an MVC sense) in
Erlang.
I come from a web software development background (PHP/js) and try to
adapt myself to Erlang philosophy but it's not always easy.
So, I'm building a game server for a browser game. I choose to stick
with MVC as it is what I know best. In my game, a user can manage one
many ships (3~5 at max I would say, and they act more like boats
except it's not on sea ...). Many ships can be engaged in one fight,
and if you control the too_big_to_not_kill ship, your ship will be in
trouble: the record representing your ship in the database may have a
lot of updates in a small range of time. (Each fire shot causing
damages for example).
Sooo (sorry this is long), there are other types of objects that i
want to handle in a similar way : craft production lines inside ships,
player bases, things that can be updated frequently and concurrently.
I decided to build a small model lib based on a gen_server that could
handle all theese requests one by one to avoid race conditions.
Because pulling a record from a db and decrasing health points in two
processes at the same time (user http requests) then updating the
records doesn't work, then i need transactions on database, i need to
lock the table, i don't like it. First question : is that wrong ?
There it is : with the help of exprecs & parse_trans, i define a
record, say 'ship' and a ship.erl module wich uses exprecs. Then i
wrote a gen_server which, from a type (like 'ship') and an ID, can
Create/Read/Update/Delete from the DB and keeps in its state my
record. I made use of gproc to map ID to pids.
Then, this gen_server has functions to work with the record.
I know this is kind of OO, and, to minimize messaging, i use a simple
pattern to update my records from a controller : i wrote a 'proc'
function ("procedure") in my gen_server API which handles all
interactions in a sort of "transaction".
... Controller calculations ...
DamgeDone = calculate_damage_done(...)
Conn = nemo:get_connection(testdba),
M = nemo:load(ship,<ship_id>,Conn),
Proc = [{get,health_points},
fun(HP,Ship) ->
Ship#ship{health_points = HP- DamgeDone}
end,
{get,health_points}],
NewHealthPoints = m:proc(M,Proc)
This "kind of object" has methods such as get, set, and can handle
funs which are passed the previous result and have to return the
updated record, or {Reply,Record} (to avoid re-calling get
health_points above). Or returning {error,Reason} which cancels any
previous modifications of the record in the same 'proc' call.
Then i plan to write "methods" in the ship module to be able to send
{method, handle_damage, Args} to my proc instead of funs.
Using records, i can use mnesia in a regular way and work with other
libs that use Mnesia. Records being abstracted, i can easily write an
adapter for PostgreSQL. Records are fast and don't need to be known by
my model code thanks to exprecs. This is not too bad.
But i would like to know what you now, because i'm not experienced.
Does exprecs negates the performance of records ? Should i avoid to
use a process for each model, because this is slow ? Then how to
easily work with race conditions ? What do you use ?
I thought about a few things : - linking model process to every caller
and closing where there's no more caller alive. The main thing is to
have only one entry point to a record in the db, but not to have all
models processes running everytime. - Handling mutiple models in a
same process ...
What do you think ?
Many thanks for reading this long,
Cheers,
Ludovic
(Sorry for bad english if any mistakes)
More information about the erlang-questions
mailing list