[erlang-questions] diameter callback module
Anders Svensson
anders.otp@REDACTED
Wed May 8 08:56:10 CEST 2013
Hi Samuel.
On Wed, May 8, 2013 at 12:19 AM, S X <erlangprogram@REDACTED> wrote:
> Hi Anders,
>
> Thanks for your quick responses and detailed comments. I am learning erlang
> during my spare time and sometimes progress slowly since no enough time to
> engage into it.
>
> Yes, like you mentioned, I started the diameter client configuration
> (capability exchange) in a separate process and wait for peer up callback
> (which I think should be same as diameter:subscribe, though I haven't try it
There is one significant difference between peer_up and subscribe:
there's a callback for each Diameter application that capabilities
exchange agrees upon (the callback module being configured per
Diameter application, while there's a single 'up' event.
> yet). Meanwhile I started workers which wait for notifications from the peer
> up callback via ets table approach to proceed with sending diameter
> messages. The problem is that if there is a lot of workers, after peer up
> notifications being received, some of them don't receive diameter answer
> from the server, instead, they still get no_connection errors from
> diameter_traffic:send function, and others can get proper diameter answers.
Are you sure the transport connection hasn't gone down? If it does
then whether or not a client is able to send will just be down to
timing.
> Sometimes, all the workers can get the answers after sending requests upon
> getting notifications from the sole peer up callback.
>
> The confusing part is that once the client receives the peer up, it should
> mean the server is ready to process diameter requests, but why still get
> no_connection errors. As you mentioned in the previous emails "If you call
> before the
>
> relevant peer_up callback then the result is {error, no_connection}."
You'll get the same result if the peer goes down before you have a
chance to send. That is, you may get a peer_up callback and try to
send but if the connection is lost before you've managed to do so then
{error, no_connection} will be the result. There'll be peer_down
callbacks to note the loss of connectivity but this can be concurrent
with your attempt to send.
> The other question is not diameter related. It is about the erlang BIF
> function register. I tried to register every process in a loop. However I
> found through debugger or using whereis function that not all process names
> can be registered properly. As I check the erlang document, it says
If by "not registered properly" you mean that whereis(Name) returns
undefined rather than the name you expect then it's probably just that
your process has died.
> register(Name, Pid) -> true. It supposes to be always successful unless the
> process name exists already then get the badarg exception.
> util:foreach_index(
> fun(N) ->
> ProcessName = list_to_atom(atom_to_list(test) ++
> integer_to_list(N)),
> register(ProcessName, spawn(executer, execute, []))
> end, MyList)
>
> Do you have any suggestions why register could fail without any
> warning/error reported?
Like I said, I don't think it's that register is failing, it's just
that your processes are dieing after registration, in which case their
registrations are removed. erlang:register/2 fails if you try to
register a non-existent process but successful registration doesn't
mean that the process will still be alive (and registered) when you
call erlang:whereis/1.
Your code above will fail if any of your processes die before calling
register. Better that each spawned process registers itself.
Anders
> Thanks!
>
> Samuel
>
>
>
> On Tue, Apr 30, 2013 at 5:04 AM, Anders Svensson <anders.otp@REDACTED>
> wrote:
>>
>> Hi Samuel.
>>
>> On Tue, Apr 30, 2013 at 4:35 AM, S X <erlangprogram@REDACTED> wrote:
>> > Hi Anders,
>> >
>> > Thanks for your explanation. Setting additional options in
>> > diameter:add_transport and retrieving them with diameter:service_info
>> > from
>> > the peer_up callback work well. And the descriptions about the peer
>> > states
>> > according to RFC3539 and 6733 make a lot of sense.
>> >
>> > The case I am trying to resolve is:
>> > Start multiple processes on one pc, each process tries to connect to one
>> > same server (obviously I will get already_connected warning, but it can
>> > keep
>> > going), in each process, then I try to send a diameter message to the
>> > server, I used to insert a certain amount of sleep time before I send
>> > the
>> > message after connecting to the server and it works ok. But I wanted to
>> > use
>> > the peer up callback to behave like a handshaking procedure among the
>> > client
>> > and the server, so the server is supposed to be ready when the client
>> > process sends the message.
>> >
>> > Now, the fact is that only one peer up callback is invoked for multiple
>> > processes. Then I tried to notify all the processes in the peer up
>> > callback
>> > when invoked and the processes send out the diameter messages. However,
>> > the
>> > processes don't receive any responses. Or sometimes only part of the
>> > processes get responses. Why inserting an amount of sleep time always
>> > works.
>> >
>> > This is a little bit confusing me, how peer up should be used properly
>> > to
>> > get the notification done right for multiple processes.
>>
>> It's difficult to say what your problem is without seeing code but
>> here are a few points.
>>
>> It's not your client processes that connect to the server. A transport
>> connection results from someone calling diameter:add_transport/2. Once
>> diameter establishes the connection, successfully performs
>> capabilities exchange, and makes peer_up callbacks, that transport
>> connection is available to any process that wants to send to the peer
>> in question. In your case, you have multiple processes that want to do
>> so, but if you're calling diameter:add_transport/2 with the same
>> config from each of those processes then you're effectively telling
>> diameter to establish multiple transport connections, only one of
>> which will succeed by default. If you want to allow multiple transport
>> connections per peer then you need to configure your service with
>> {restrict_connections, false}, but the peer need not support this (see
>> 2.1 in RFC 6733) so may reject all but one CER. Without
>> restrict_connections it's diameter that disallows multiple
>> connections.
>>
>> Given that you want multiple client processes to send from (not wrong,
>> but why?), I would treat these as workers that have nothing to do with
>> configuration. That is, call diameter:start_service/2 and
>> diameter:add_transport/2 from elsewhere and have your workers kick
>> into action after capabilities exchange.
>>
>> Like you said, your workers want to know when the peer becomes
>> available. There are two ways to get notification: a peer_up callback
>> or a diameter event. The latter are subscribed to with
>> diameter:subscribe/1 so one way is for each worker to subscribe to
>> events and react to them. If you want to use peer_up as a trigger then
>> you need to code some way for the callback to notify your worker
>> processes. There are plenty of ways to do this, none of which are
>> diameter-specific. For example: maintain your worker pids in an ets
>> table that the callback examines; have workers register with a
>> registered gen_server that finds out about callbacks; spawn workers as
>> a result of peer_up. Sleep is never a reliable solution.
>>
>> Anders
>>
>>
>> >
>> > Thanks for any suggestions!
>> >
>> > Samuel
>> >
>> >
>> >
>> >
>> > On Thu, Apr 25, 2013 at 5:40 AM, Anders Svensson <anders.otp@REDACTED>
>> > wrote:
>> >>
>> >> On Wed, Apr 24, 2013 at 5:23 PM, S X <erlangprogram@REDACTED> wrote:
>> >> > Thanks a lot for your comments.
>> >> >
>> >> > The diameter:call function allows to send extra arguments. It works
>> >> > fine
>> >> > after changing the callback function signatures accordingly. This is
>> >> > very
>> >> > useful when I want to notify the other process to do the next thing.
>> >> >
>> >> > Before sending a diameter message, we call diameter:add_transport to
>> >> > connect
>> >> > to a peer and peform CER/CEA capability information exchange. On the
>> >> > caller
>> >> > side, let's say the client callback module, the peer_up will be
>> >> > invoked
>> >> > when
>> >> > CER/CEA is completed. Why it doesn't have the similar mechanism like
>> >> > diameter:call to allow insert additional arguments so we can utilize
>> >> > them,
>> >> > for example, notify the others to send diameter messages?
>> >>
>> >> No particular reason aside from history and that the need hasn't come
>> >> up. As it is, you can pass arbitrary options to
>> >> diameter:add_transport/2 (history again) and retrieve these in a
>> >> callback with diameter:service_info(PeerRef), so that can be used a
>> >> substitute for extra arguments in this case.
>> >>
>> >> > diameter:add_transport is a sync call, but it doesn't mean you can
>> >> > send
>> >>
>> >> add_transport *doesn't* wait for the config it's passed to result in a
>> >> connection before returning. (It might never happen for one.)
>> >>
>> >> > diameter messages successfully when the function returns, i.e. it
>> >> > usually
>> >> > gets {error, no_connection} or { error, timeout }(this might because
>> >> > of
>> >> > the
>> >> > server side) if you call diameter:call right after
>> >> > diameter:add_transport.
>> >>
>> >> This is because the peer won't be ready to respond to requests until
>> >> capabilities exchange has completed (at least). If you call before the
>> >> relevant peer_up callback then the result is {error, no_connection}.
>> >> After peer_up the result will be {error, timeout} if the peer doesn't
>> >> answer. One case in which this is expected is after a connection has
>> >> been reestablished following an exchange of 3 x DWR/DWA. (ie. RFC 3539
>> >> DOWN -> REOPEN -> OKAY.) Since both ends of the connection do this,
>> >> the client can consider the connection to be reestablished before the
>> >> server. If the client sends a request before the server is done with
>> >> it's exchange (ie. reached OKAY) then RFC 6733 says it should discard
>> >> the request, resulting in a timeout on the client end.
>> >>
>> >> > So is there any reason why not allowing to add additional arguments
>> >> > and
>> >> > use
>> >> > them in peer_up callback function? Since it means capability exchange
>> >> > is
>> >> > done and the peer is ready, at this point it should be safe to send
>> >> > diameter
>> >> > messages to the peer.
>> >>
>> >> See above.
>> >>
>> >> Anders
>> >>
>> >>
>> >> >
>> >> > Any suggestions?
>> >> >
>> >> > Thanks!
>> >> >
>> >> > Samuel
>> >> >
>> >> >
>> >> > On Tue, Apr 16, 2013 at 7:22 AM, Anders Svensson
>> >> > <anders.otp@REDACTED>
>> >> > wrote:
>> >> >>
>> >> >> On Sun, Apr 14, 2013 at 7:01 PM, S X <erlangprogram@REDACTED>
>> >> >> wrote:
>> >> >> > Hello,
>> >> >> >
>> >> >> > Based on the erlang diameter library and the sample code, I want
>> >> >> > to
>> >> >> > start
>> >> >> > multiple diameter client processes in one erlang node(one client
>> >> >> > IP),
>> >> >> > and
>> >> >> > the client needs to define a diameter_app callback module for
>> >> >> > certain
>> >> >> > application, for example:
>> >> >> >
>> >> >> > -define(SERVICE(Name), [{'Origin-Host', ?L(Name) ++
>> >> >> > ".example.com"},
>> >> >> > {'Origin-Realm', "example.com"},
>> >> >> > {'Vendor-Id', 193},
>> >> >> > {'Product-Name', "Client"},
>> >> >> > {'Auth-Application-Id',
>> >> >> > [?DIAMETER_APP_ID_COMMON,
>> >> >> > ?DIAMETER_APP_ID_CCRA]},
>> >> >> > {application, [{alias, ?APP_CCR_ALIAS},
>> >> >> > {dictionary,
>> >> >> > ?DIAMETER_DICT_CCRA},
>> >> >> > {module,
>> >> >> > client_cb_ccra}]}]).
>> >> >> >
>> >> >> >
>> >> >> > First question:
>> >> >> > how the diameter library handles this situation? Will all diameter
>> >> >> > client
>> >> >> > processes share one single diameter_app callback module
>> >> >> > "client_cb_ccra"
>> >> >> > or
>> >> >> > it will automatically attach different instance of the callback
>> >> >> > module
>> >> >> > (process) to the different client process by using spawn_monitor?
>> >> >> > So
>> >> >> > from
>> >> >> > the callback handle_answer in "client_cb_ccra" I can notify the
>> >> >> > proper
>> >> >> > client proce by just calling client:notify() something.
>> >> >>
>> >> >> diameter doesn't know anything about your client process. If you
>> >> >> want
>> >> >> a callback to be able to contact the client process associated with
>> >> >> the service in question (ie. the service whose name the callback
>> >> >> gets
>> >> >> as an argument) then you need to give the callback the means to do
>> >> >> so.
>> >> >> One way would be to map the service name to your process (eg. it's
>> >> >> the
>> >> >> registered name of your client process), another would be to pass
>> >> >> some
>> >> >> identification as extra arguments to the callbacks. (Eg. {module,
>> >> >> [client_cb_ccra, X]} in the config above.)
>> >> >>
>> >> >> > Second question:
>> >> >> > Notice that diameter:call allows to set extra arguments, so I was
>> >> >> > wondering
>> >> >> > I could set some data like:
>> >> >> > diameter:call(Name, ?APP_CCR_ALIAS, CCR, [{extra, [self()]}]).
>> >> >> > which sets the client process ID and I hope to deliver to the
>> >> >> > callback
>> >> >> > module "client_cb_ccra" and when the callback module knows which
>> >> >> > client
>> >> >> > process sends request and response accordingly, for example using
>> >> >> > the
>> >> >> > client
>> >> >> > process ID in the callback function handle_answer.
>> >> >>
>> >> >> You can do that, but what is it you're trying to accomplish? if it's
>> >> >> handle_answer that's supposed to communicate something then it
>> >> >> already
>> >> >> does: the return value of handle_answer is returned by
>> >> >> diameter:call.
>> >> >>
>> >> >> > However, I don't want pack the extra arguments into the diameter
>> >> >> > packet
>> >> >> > since the diameter server doesn't know what they are and the extra
>> >> >> > ones
>> >> >> > are
>> >> >> > not part of standard diameter packet.
>> >> >>
>> >> >> Not sure what you mean here. There's no way for your client to send
>> >> >> the server anything other than a Diameter message.
>> >> >>
>> >> >> > Now I changed the pick_peer callback signatures to allow extra
>> >> >> > arguments,
>> >> >> > pick_peer([Peer | _], _, _SvcName, _State, A)
>> >> >> >
>> >> >> > But I got encoding error in the callback prepare_request
>> >> >> >
>> >> >> > =ERROR REPORT==== 14-Apr-2013::11:40:41 ===
>> >> >> > Error in process <0.175.0> with exit value:
>> >> >> > {undef,[{client_cb_ccra,prepare_request,[{diameter_packet,
>> >> >>
>> >> >> The arity of your callback probably doesn't agree with the number of
>> >> >> extra arguments you've specified: prepare_request will also get your
>> >> >> extra arguments.
>> >> >>
>> >> >>
>> >> >> >
>> >> >> > >
>> >> >> > > > >{diameter_header,1,undefined,undefined,undefined,3958849953,3958849953,undefined,undefined,undefined,undefined},undefined,{diameter_rfc4006_cc_CCR,["who",";","142745567...
>> >> >> >
>> >> >> > {error,encode}
>> >> >> >
>> >> >> > In overall, I read the online documents, which have limited
>> >> >> > information
>> >> >> > on
>> >> >> > how to use the extra arguments in the library and don't quite get
>> >> >> > how
>> >> >> > to
>> >> >> > utilize the extra arguments to do something tricky,
>> >> >> >
>> >> >> > Are there any suggestions on how to deal with multiple client
>> >> >> > processes?
>> >> >>
>> >> >> Not sure I understand what problem it is you're trying to solve.
>> >> >> Multiple processes invoking diameter:call is nothing strange. You
>> >> >> typically just return something useful (eg. the answer message from
>> >> >> the peer) from handle_answer, which diameter:call then returns for
>> >> >> the
>> >> >> caller to deal with.
>> >> >>
>> >> >> Anders
>> >> >>
>> >> >>
>> >> >> > Thanks a lot!
>> >> >> >
>> >> >> > Samuel
>> >> >> >
>> >> >
>> >> >
>> >
>> >
>
>
More information about the erlang-questions
mailing list