i am writing records into mnesia which should be kept there only for an allowed time (24 hours). after 24 hours, before a user modifies part of them, the system should remove them automatically. forexample, a user is given free airtime (for voice calls) which they should use in a given time. if they do not use it, after 24 hours, the system should remove these resource reservation from the users record.
Now, this has brought in timers. an example of a record structure is:
-record(free_airtime, { reference_no, timer_object, %% value returned by timer:apply_after/4 amount }).
The timer object in the record is important because in case the user
finally puts to use the resources reserved before they are timed out
(or if they time out),the system can call timer:cancel/1
so as to relieve
the timer server from this object.
Now the problem, i have two ways of handling timers on these records:
Option 1: timers handled within the transaction
reserve_resources(Reference_no,Amnt)-> F = fun(Ref_no,Amount) -> case mnesia:read({free_airtime,Ref_no}) of [] -> case mnesia:write(#free_airtime{reference_no = Ref_no,amount = Amount}) == ok of true -> case timer:apply_after(timer:hours(24),?MODULE,reference_no_timed_out,[Ref_no]) of {ok,Timer_obj} -> [Obj] = mnesia:read({free_airtime,Ref_no}), mnesia:write(Obj#free_airtime{timer_object = Timer_obj}); _ -> mnesia:abort({error,failed_to_time_object}) end; false -> mnesia:abort({error,write_failed}) end; [_] -> mnesia:abort({error,exists,Ref_no}) end end, mnesia:activity(transaction,F,[Reference_no,Amnt],mnesia_frag).
About the above option.
Mnesia docs say that transactions maybe repeated by the tm manager (due to some reason)
until they are successful, and so when you put code which is io:format/2
or any other which has nothing to do with
writes or reads, it may get executed several times. This statement made me pause at this point
and think of a way of handling timers out of the transaction it self, so i modified the code as
follows:
Option 2: timers handled outside the transaction
reserve_resources(Reference_no,Amnt)-> F = fun(Ref_no,Amount) -> case mnesia:read({free_airtime,Ref_no}) of [] -> P = #free_airtime{reference_no = Ref_no,amount = Amount}, ok = mnesia:write(P), P; [_] -> mnesia:abort({error,exists,Ref_no}) end end, Result = try mnesia:activity(transaction,F,[Reference_no,Amnt],mnesia_frag) of Any -> Any catch exit:{aborted,{error,exists,XX}} -> {exists,XX} E1:E2 -> {error,{E1,E2}} end, on_reservation(Result). on_reservation(#free_airtime{reference_no = Some_Ref})-> case timer:apply_after(timer:hours(24),?MODULE,reference_no_timed_out,[Some_Ref]) of {ok,Timer_obj} -> [Obj] = mnesia:activity(transaction,fun(XX) -> mnesia:read({free_airtime,XX}) end,[Some_Ref],mnesia_frag), ok = mnesia:activity(transaction,fun(XX) -> mnesia:write(XX) end,[Obj#free_airtime{timer_object = Timer_obj}],mnesia_frag); _ -> ok = mnesia:activity(transaction,fun(XX) -> mnesia:delete({free_airtime,XX}) end,[Some_Ref],mnesia_frag), {error,failed_to_time_object} end; on_reservation(Any)-> Any.
The code to handle time out of the reservation:
reference_no_timed_out(Ref_no)-> do_somethings_here..... then later remove this reservation from the database....below.. ok = mnesia:activity(transaction,fun(XX) -> mnesia:delete({free_airtime,XX}) end,[Ref_no],mnesia_frag).
Now i thought that in option 2, i am safer by keeping the timer processing code out, even when mnesia_tm re-executes the transaction due to its reasons , this piece of code is not run twice (i avoid having several timer objects against the same record).
Question 1: Which of these two implementations is right? and/or wrong? Tell me (also) wether both of them are wrong
Question 2: The module timer, is it well suited for handling large numbers of timer jobs in production?
Question 3: As compared to Sean Hinde's timer_mn-1.1, which runs on top of mnesia, is the timer module (possibly running on top of Ets tables) less capable (for real) in production? (am asking this because using Sean Hinde's timer_mn on a system which itself is using mnesia appears to be a problem in terms schema changes, node problems e.t.c)
If any one has another way of handling timer related problems with mnesia, update me thanx guys...