[erlang-questions] Additional question regarding: gen_server vs gen_fsm

Chandru chandrashekhar.mullaparthi@REDACTED
Wed Oct 10 12:00:17 CEST 2007


Hi,

On 10/10/2007, David Budworth <dbudworth@REDACTED> wrote:
> Hi folks,
>
> YC's topic titled gen_server vs gen_fsm got me wondering
>
> Note for the reader: Sorry, just realized this is really long, but I'm
> really curious as to how/if this can be solved.  The last 3/4 of this is
> more bouncing ideas off people.
>
> I'm investigating writing an order routing system using erlang and can
> imagine a few different ways to deal with it (also an erlang newbie)
>
> in our case, the duration of a transaction can be months.
> So I'm assuming we really do need to persist state via mnesia (which I'll
> bug people about in upcoming messages, I'm sure).
>
> aside from that, there's a natural state progression for an order on an
> exchange, they tend to have states that go from:
> new -> confirmed -> partial_fill -> filled
> (and canceled, expired, etc depending on what really happens).
>
> At first glance of gen_fsm vs gen_server, it seems like gen_fsm is really a
> fancyish tracking mechanism built on top of (or really similar to)
> gen_server to keep state transition plumbing out of the 'business logic'
> area.
>
>
> so basically, the idea is (just looking for confirmation):
> Current State: confirmed
>
> confirmed(FillEvent,StateData) -> signal_done, {done,DoneStateData};
> confirmed(PartialFillEvent,StateData) ->
> {partial_fill,UpdatedStateData}.
>
> partial_fill(FillEvent,StateData) -> signal_done(),
> {done,DoneStateData}.
>
> something to that effect.
>
> so the question is, it seems like I'd have to manually persist "StateData"
> on each event handled to survive process/host failures and basically when
> detecting a process failure I'd have to spawn a new gen_fsm and register it
> (say with order id or something) passing in the most recently persisted
> state.
>
> Am I on the right track?

Yes, you are. You don't *have* to register it (I'm assuming you are
talking about process name registration.)

> I guess the only thing odd here, or rather, what I'm not entirely sure how
> it's handled is that with everything happening async in general, how does
> one guarantee that a message isn't lost while in transit and a host fails?
>
> coming from the JMS world, you receive a message in a TX, then do your work,
> publish a new message in that TX then commit.  If you die in the middle, the
> message is re-delivered to another listener sitting there.
>
> in the OTP world, what is the equivalent to this?
> meaning:
> SomeProcessThatSendsAnEvent:
>
> OrderHandlerPID ! MessageToProcess.
>
> OrderHandler
>    receive
>      Msg ->  start_doing_stuff(), crash().
>
> it seems like the only way to guarantee delivery is to make everything
> Request/Response synchronized.
> so you send a message then wait for an ack.  And if it doesn't come, you
> lookup/respawn/whatever a pid to re-send to until it succeeds.
>
> So if a process takes a long time, I'm blocking the sender.   Whereas in JMS
> (or JavaSpaces really) I know that if I send a message, that the message
> broker will Never(tm) lose the message and it will eventually be handled.
>
> Only way I see to make fast senders and reliable delivery would be to send a
> message to an intermediate process that persists/replicates to a buddy, and
> have the consumers send "Message Consume Requests" to that same intermediate
> process which will deliver the message and mark/delete it (still with a race
> condition since I may have deleted before sending or sent before deleting)
> giving me dups.
>
> it's almost as though what I really need is to use mnesia as my message bus
> so the "consumer" will do a select, process and submit an update.  This only
> works if mnesia supports operations similar to:
> begin tx
> insert (response values) in to msg table
> update (request row with DONE status) table where (request row) exists %% or
> delete the row, either way
> commit

mnesia does support this. And yes - to be really sure that every
message is handled, and only handled once, you have to use mnesia as
your message bus. You can have message senders insert into an mnesia
table and OrderHandler processes take one at a time and process them.
When you are done, you either change the state of the entry to DONE or
move it into another table in a single transaction.

> Is this type of thing just not what erlang is meant for?

Definitely. We do something exactly like this for our Third Party
Gateway. Messages from 3rd parties have to sent to mobile telephones
once and only once. We use mnesia for message queues. Each queue is
handled by a single process which spawns workers for each request in
the queue. The worker deletes the entry from the queue table when it
has successfully sent the message.

If a node restarts in the middle of queue processing, there might be
some entries in the database which have a status 'PROCESSING' but you
can't be sure whether the message was processed but the table did not
get updated, or whether the message did not get processed at all. Such
messages, we send failure reports to our 3rd parties which indicate
the uncertainity of that transaction, and delete them from the queue.

cheers
Chandru



More information about the erlang-questions mailing list